home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Just Call Me Internet
/
Just Call Me Internet.iso
/
prog
/
atari
/
m2
/
cat3src
/
cat
/
data.i
< prev
next >
Wrap
Text File
|
1997-10-26
|
136KB
|
3,668 lines
IMPLEMENTATION MODULE data;
(*$R-,S-*)
(*==============================================================*
* Modul: CAT-Datenbank, neue Version *
* Autor: Johannes Gttker-Schnetmann *
* erstellt am: 26.07.1991 *
* letzte nderung am: 30.10.1993 *
* Version: 1.0 *
* Interne Version: V#0017 *
* Achtung: Mu immer mit $R- bersetzt werden, da R= nicht *
* funktioniert! *
*==============================================================*
Das Modul implementiert eine nach Gruppen getrennte Datenbank fr die
Nachrichten der Maus. Weiterhin werden pro Gruppe drei Positionen
verwaltet: letzte gelesene Position, Position der ersten neuen Msg und
letzte Msg der Datenbank.
letzte Msg der Datenbank.
In diesem Modul verlasse ich mich erstmal auf die Fhigkeit vom
MM2-Storage, am Programmende alles wieder freizugeben, denn das soll
es ja gerchteweise knnen. Dann kann man sich den dazu ntigen (redundanten)
Code sparen!
Weitere Optimierungen: Hashtabelle, intelligentere Pufferung, um auch bei
wenig Speicher noch Geschwindigkeit zu erreichen
- Hashing-Konzept ist noch nicht auf flexiblere Pufferung vorbereitet,
die wird aber auch noch nicht benutzt.
- Achtung, bei WriteBlock mu jetzt im Normalfall eine Anpassung der
crc erfolgen!
*----------------------------------------------------------------------------
* Datum Vers. Autor nderung (Arbeitsbericht)
*----------------------------------------------------------------------------
* 26.07.91 0001 JGS Erste Version
* 27.08.91 JGS Listen einlesen (Gruppenliste, Adressenliste)
* 28.08.91 JGS Deklarationen, Konzept..
* 02.10.91 JGS Naja, alles mgliche -> Beginn der Arbeiten -> 11.10
* 12.10.91 0002 JGS Lesen sollte jetzt klappen,
* Konzept fr Schreiben angefangen
* 14.03.92 0003 JGS erstes Konzept fr den neuen Parameterblock
* 19.03.92 0004 JGS Liste der Gruppennamen von auerhalb erweiterbar
* 23.03.92 0005 JGS Diverse Anpassungen, Msgs schreiben angefangen /24.
* 02.04.92 0006 JGS Nummer und Position der neuen angepat
* 04.04.92 0007 JGS Anpassung an neue Listendeklaration
* 06.04.92 0008 JGS Hashtabelle
* 08.04.92 JGS PrepareToWrite/GroupNumber verbessert
* 15.04.92 0009 JGS WatchDog-Protokoll/Dupecheck/Statusmeldungen
* 16.04.92 JGS ..weiter Statusmeldungen, Dupecheck.., Suchen
* 24.04.92 0010 JGS Suchen rckwrts vorbereitet
* 04.10.92 0011 JGS Beginn der Umstellung auf das neue Datenbankformat..
* 22.10.92 0012 JGS Neues Format vorlufig fertig, Verkettung per RId eingebaut.
* -- etliche Kleinigkeiten --
* 23.11.92 0013 JGS Hochsicherheitstrakt, die erste
* 23/24.12 0014 JGS Verwaltung der ungelesenen Msgs.
* 27.12.92 JGS Verkettung anhand der ersten Msgzeile
* 31.12.92 JGS Baum lschen, Baum durchlaufen
* 09.01.93 0015 JGS Vererben-Flag
* 13.08.93 JGS Suchroutine auf mehrere Begriffe mit Verknpfungen umgestellt.
* 02.10.93 0016 JGS Usenet-Verkettung herstellen
* 30.10.93 0017 DS Format der GRUPPEN.POS gendert, ist nun dynamisch
*----------------------------------------------------------------------------
*)
(* MegaMax-Module *)
FROM SYSTEM IMPORT ADR, TSIZE, ADDRESS, CADR, BYTE, CALLSYS;
FROM Characters IMPORT CR, LF, SUB;
FROM Storage IMPORT ALLOCATE, DEALLOCATE;
IMPORT Lists;
IMPORT Storage;
IMPORT BinOps;
IMPORT Block;
IMPORT StrConv;
IMPORT Strings;
(* Cat-Module *)
FROM Void IMPORT v;
FROM MTPaths IMPORT DataPath;
(*
FROM TimeService IMPORT CardToTime, CardToDate;
*)
FROM ZCalcCrc IMPORT CalcCrc, CalcIdCrc, CalcCrcArray;
FROM UserInformation IMPORT UserBLK;
FROM CatTypes IMPORT String1023, Str1023Ptr, DateType, BigTextPtr;
FROM GroupComment IMPORT PrepareID;
FROM VDIUtil IMPORT isKey;
IMPORT CatFiles;
IMPORT CatTypes;
IMPORT CatLog;
IMPORT MTE;
IMPORT Hashing2;
(* IMPORT BoyerMoore; *)
IMPORT SearchHelp;
IMPORT Find2;
IMPORT CatGlobal;
IMPORT QuickSort;
IMPORT Varnames;
IMPORT ConfVars;
IMPORT GroupSelect;
IMPORT dataSys;
IMPORT AssFuncs;
IMPORT ConvertDate;
IMPORT WildCards;
IMPORT Protokoll;
(* MagicLib *)
IMPORT MagicAES;
IMPORT MagicDOS;
IMPORT MagicStrings;
IMPORT MagicConvert;
IMPORT FileSys;
IMPORT Mintbind;
FROM MagicSys IMPORT lINTEGER;
(* MagicTools *)
IMPORT mtAlerts;
IMPORT mtTextfiles;
IMPORT mtAppl;
(*-- Import der Datenbankstrukturen --*)
FROM dataSys IMPORT
FileHeaderType, dbHeaderLength, dbCatMagic, dbVersion, dbVersionMagic,
standardHeader,
mVon, mAn, mMId, mRId, mBox, mName, mRefNr, mDistribution, mGate, mMime, mReplyTo, mSender, mFollowup, mUnknown, mPrivateBytes,
dupeInfoPtr, dupeInfoType, Terminator,
pBlockPtr, pBlock, pInfoPtr, pInfoType, grPosType, posType, maxGroup,
private, empty, notSaved, personalName,
(* Statusbits einer Msg *)
bGelesen, bFiltered, bInteressant, bTeilloeschung, bTotalloeschung,
bKommentieren, bAntworten, bUser1, bUser2, bVererben,
(* Vorlufig bis bei der Maus die Message-Ids diesen Namen auch verdienen: *)
bOldDupe, bOwnMessage, bComToOwnMessage, bOldComToOwnMessage;
CONST editSecureBytes = 15; (* Wieviele Bytes sollen fr den Editor beim Text zur Sicherheit *)
(* zustzlich angefordert werden? *)
CONST fillBytePersonal = 0314C; (* Fllbyte fr das Alignen der PersonalInfo *)
CONST GRInf = 'gruppen.inf';
GRPos = 'gruppen.pos';
ADRInf = 'adr.inf';
rawDate = 'xx.xx.xx xx:xx';
rawGroupname = 'gruppe00';
rawPrivatename = 'private';
tabTag = '.tab';
parTag = '.par';
datTag = '.dat';
tabWild = '*.tab';
parWild = '*.par';
allWild = '*.*';
IDError = 'ID-Fehler';
additional = 1000; (* Platz fr soviele neue Msgs erstmal vorsehen *)
CONST minDatBuffer = 32*1024; (* Erst ab 32k puffern *)
maxDatBuffer = MAX(CARDINAL)-1; (* bis zu 64k puffern *)
TYPE parArray = ARRAY[0..MAX(CARDINAL)] OF pBlock;
parArrayPtr = POINTER TO parArray;
tabArray = ARRAY[0..MAX(CARDINAL)] OF CARDINAL;
tabArrayPtr = POINTER TO tabArray;
OneGroupHandle = POINTER TO oneGroupHandle;
oneGroupHandle =
RECORD
group,
anz : CARDINAL; (* Anzahl der Nachrichten in dieser Gruppe *)
tabHandle,
parHandle,
datHandle : INTEGER; (* handles der entsprechenden Dateien *)
parBuff : parArrayPtr; (* Wenn # NIL, dann gepuffert *)
tabBuff : tabArrayPtr; (* dto. *)
datBuff : CatTypes.BigTextPtr;
minPar, (* erster Parameterblock, der im Speicher steht *)
ParAnz, (* maximale Anzahl, die noch im Speicher gehalten werden kann *)
ParBuffAnz, (* Anzahl der gepufferten Parameterblcke *)
minTab, (* erste crc, die im Speicher steht *)
TabAnz, (* maximale Anzahl, die im Speicher gehalten werden kann *)
TabBuffAnz : CARDINAL; (* Anzahl der gepufferten crcs *)
DatAnz, (* Anzahl der belegbaren Bytes im DAT-AppendBuffer *)
DatBuffAnz : CARDINAL; (* Anzahl belegte Bytes im DAT-AppendBuffer *)
DatBuffStart : LONGCARD; (* Dateiposition, an der der DAT-Buffer beginnt *)
DatSize : LONGCARD; (* Gre der .DAT zu Testzwecken *)
(* Noch nicht beachtet: *)
writeThrough : BOOLEAN; (* trotz Pufferung Schreiboperationen durchlassen *)
(* Hashing *)
hash : Hashing2.hashHandle;
open : BOOLEAN;
touchlastpos : BOOLEAN; (* Position letzte gelesene neu setzen oder nicht *)
(* -> evtl. besseres Konzept *)
END;
(*
TYPE posType = (aktuellePos, neuePos, letztePos, unreadPos, unreadCount);
grPosType =
RECORD
head : FileHeaderType;
save : ARRAY posType OF CARDINAL;
pos : POINTER TO ARRAY[0..maxGroup] OF ARRAY[aktuellePos..unreadCount] OF CARDINAL;
posGroups : CARDINAL;
END;
*)
(* Die Nummer 0 ist ab sofort die fr die persnlichen Msgs, d.h. die Nummerierung auf
* der Platte stimmt nicht mehr ganz, bleibt aber zunchst beim alten und mu also
* fr den Zugriff umgerechnet werden
*)
VAR grPos : grPosType;
VAR grPosRead : BOOLEAN; (* Wurde die Tabelle schon gelesen? *)
VAR emptyString : ARRAY[0..10] OF CHAR;
VAR dangerousDupeMode : BOOLEAN;
usenetChaining : BOOLEAN;
isInSearch : BOOLEAN; (* gerade luft eine Suche *)
PROCEDURE isOldHeader(REF head : FileHeaderType):BOOLEAN;
BEGIN
WITH head DO
RETURN
(CatMagic # dbCatMagic) OR
(Version # dbVersion) OR
(VersionMagic # dbVersionMagic);
END;
END isOldHeader;
(*------ Prozeduren fr dynamisches GRUPPEN.POS -------------------*)
PROCEDURE CreatePosArray (used: CARDINAL): BOOLEAN;
VAR i : CARDINAL;
z : posType;
BEGIN
grPos.usedGroups := used;
grPos.posGroups := grPos.usedGroups;
IF grPos.posGroups < maxGroup - 50
THEN
INC (grPos.posGroups, 50);
ELSE
grPos.posGroups := maxGroup;
END;
(* Jetzt Speicher allozieren *)
ALLOCATE (grPos.pos, LONG(grPos.posGroups) * TSIZE (dataSys.onePos));
IF grPos.pos = NIL THEN
grPos.usedGroups := 0;
grPos.posGroups := 0;
RETURN FALSE
END;
FOR i := 0 TO grPos.posGroups -1 DO
FOR z := aktuellePos TO unreadCount DO
grPos.pos^[i, z] := empty
END;
END;
RETURN TRUE;
END CreatePosArray;
PROCEDURE GetOnePos (group: CARDINAL; subIdx: dataSys.posType): CARDINAL;
BEGIN
IF group < grPos.usedGroups
THEN
RETURN grPos.pos^[group, subIdx]
END;
RETURN empty;
END GetOnePos;
PROCEDURE SetOnePos (group: CARDINAL; subIdx: dataSys.posType; value: CARDINAL);
VAR newSize : CARDINAL;
newArray: POINTER TO ARRAY [0..maxGroup] OF dataSys.onePos;
i : CARDINAL;
z : posType;
wasRead : BOOLEAN;
BEGIN
wasRead := grPosRead;
IF ~grPosRead THEN IF ~ReadPos() THEN RETURN END END;
IF group >= grPos.posGroups
THEN
(* Realloc, Array vergrern *)
IF newSize < maxGroup - 20
THEN
newSize := group+20;
ELSE
newSize := maxGroup;
END;
ALLOCATE (newArray, LONG(newSize) * TSIZE (dataSys.onePos));
IF newArray = NIL THEN RETURN END;
Block.Clear (newArray, LONG(newSize) * TSIZE (dataSys.onePos));
FOR i := grPos.posGroups TO newSize DO
FOR z := aktuellePos TO unreadCount DO
grPos.pos^[i, z] := empty
END;
END;
Block.Copy (grPos.pos, LONG(grPos.posGroups) * TSIZE (dataSys.onePos), newArray);
DEALLOCATE (grPos.pos, 0);
grPos.posGroups := newSize;
grPos.pos := ADDRESS(newArray);
END;
grPos.usedGroups := BinOps.HigherCard (grPos.usedGroups, group+1);
grPos.pos^[group, subIdx] := value;
END SetOnePos;
(*--- Datumsprozeduren ---*)
(*
PROCEDURE GetActualDate(VAR l : LONGCARD);
(* GemDos-Datum in einen Cat-Datums Longcard *)
VAR z1,z2,z3,z4 : CARDINAL;
BEGIN
z1 := MagicDOS.Tgettime();
CardToTime(z1, z2,z3,z4); (* Stunde, Minute, Sekunde *)
l := LONG(z3); (* Minute *)
l := l + LONG(z2) * 100; (* Stunde *)
z1 := MagicDOS.Tgetdate();
CardToDate(z1, z2,z3,z4); (* Jahr, Monat, Tag *)
IF z2 >= 1990 THEN DEC(z2, 1990) ELSE z2 := 0 END;
l := l + LONG(z4) * 10000; (* Tag *)
l := l + LONG(z3) * 1000000; (* Monat *)
l := l + LONG(z2) * 100000000; (* Jahr *)
END GetActualDate;
*)
PROCEDURE mDateStr2Long(REF str : ARRAY OF CHAR; start : CARDINAL):LONGCARD;
(* Maus-Datumsstring in ein Cat-Datum umwandeln *)
(* Format Jahr|Jahr|Jahr|Jahr|Monat|Monat|Tag|Tag|Stunde|Stunde|Minute|Minute *)
(* Jahr := Jahr - 1990 *)
(* Format Jahr|Jahr|Monat|Monat|Tag|Tag|Stunde|Stunde|Minute|Minute *)
VAR year : CARDINAL;
date : LONGCARD;
dt2 : LONGCARD;
pos : CARDINAL;
tmp : ARRAY [0..11] OF CHAR;
(* !!! Evtl. noch ein check, ob's keinen berlauf gibt.. *)
BEGIN
(*
(* Minuten *)
date := LONG(ORD(str[11+start]) - ORD('0'));
date := date + LONG(ORD(str[10+start]) - ORD('0')) * 10;
(* Stunden *)
date := date + LONG(ORD(str[9+start]) - ORD('0')) * 100;
date := date + LONG(ORD(str[8+start]) - ORD('0')) * 1000;
(* Tage *)
date := date + LONG(ORD(str[7+start]) - ORD('0')) * 10000;
date := date + LONG(ORD(str[6+start]) - ORD('0')) * 100000;
(* Monate *)
date := date + LONG(ORD(str[5+start]) - ORD('0')) * 1000000;
date := date + LONG(ORD(str[4+start]) - ORD('0')) * 10000000;
(* Jahre fehlen, sind im Moment unten *)
date := date + LONG(year) * 100000000;
*)
(* Jahre *)
year := ORD(str[3+start]) - ORD('0');
year := year + (ORD(str[2+start]) - ORD('0')) * 10;
year := year + (ORD(str[1+start]) - ORD('0')) * 100;
year := year + (ORD(str[ start]) - ORD('0')) * 1000;
IF year >= 1990 THEN DEC(year, 1990) ELSE year := 0 END;
(* Neue Routine *)
Strings.Copy (str, start+4, 8, tmp, v.bool);
pos := 0;
dt2 := StrConv.StrToLCard (tmp, pos, v.bool);
dt2 := dt2 + LONG(year) * 100000000;
IF dt2 < 01010000
THEN
(* Es mu mindestens der 1.1.90 sein. *)
dt2 := 1010000;
END;
RETURN dt2;
END mDateStr2Long;
(*
PROCEDURE InsertDay(y, m, d : CARDINAL; VAR date : ARRAY OF CHAR);
(* Wochentag an die erste Stringstelle einfgen *)
VAR w : CARDINAL;
str : ARRAY[0..3] OF CHAR;
MM7 : ARRAY[1..12] OF CARDINAL;
wDays : ARRAY [0..19] OF CHAR;
BEGIN
MM7[1] := 0; MM7[2] := 3; MM7[3] := 3; MM7[4] := 6;
MM7[5] := 1; MM7[6] := 4; MM7[7] := 6; MM7[8] := 2;
MM7[9] := 5; MM7[10]:= 0; MM7[11]:= 3; MM7[12]:= 5;
IF (y < 1900) OR (y > 1999) THEN
w := 7
ELSE
DEC(y, 1900);
IF m > 12 THEN m := m MOD 12 + 1; END; (* verhindert Absturz! *)
w := ( (y MOD 7) + ((y DIV 4) MOD 7) + MM7[m] + d ) MOD 7;
END;
MagicStrings.Assign ('SoMoDiMiDoFrSa??',wDays);
MagicStrings.Copy(wDays, w*2,2, str);
MagicStrings.Append(', ', str);
MagicStrings.Insert(str, date, 0);
END InsertDay;
*)
PROCEDURE Long2DateStr(date : LONGCARD; VAR str : ARRAY OF CHAR);
(* Cat-Datum in ein menschenlesbares verwandeln *)
VAR c1, year, y,m,d : CARDINAL;
dt : ConvertDate.Date;
ti : ConvertDate.Time;
dstr : ARRAY [0..40] OF CHAR;
BEGIN
ConvertDate.CatDate2Datim (date, dt, ti);
ConvertDate.DateToText (dt, "DD.MM.YY", str);
ConvertDate.TimeToText (ti, " HH:MM", dstr);
MagicStrings.Append (dstr, str);
MagicStrings.Copy('MoDiMiDoFrSaSo??', ORD(ConvertDate.WeekDay(dt))*2,2, dstr);
MagicStrings.Append(', ', dstr);
MagicStrings.Insert(dstr, str, 0);
(*
MagicStrings.Assign (rawDate,str);
(* Jahr *)
year:= SHORT(date DIV 100000000);
date:= date MOD 100000000;
INC(year, 1990);
y := year;
year := year MOD 100;
str[6] := CHR(year DIV 10 + ORD('0')); (* 'xx.xx.?x xx:xx' *)
str[7] := CHR(year MOD 10 + ORD('0')); (* 'xx.xx.?? xx:xx' *)
(* Monat *)
c1 := SHORT(date DIV 10000000);
m := c1 * 10;
date:= date MOD 10000000;
str[3] := CHR(c1 + ORD('0')); (* 'xx.?x.?? xx:xx' *)
c1 := SHORT(date DIV 1000000);
m := m + c1;
date:= date MOD 1000000;
str[4] := CHR(c1 + ORD('0')); (* 'xx.??.?? xx:xx' *)
(* Tag *)
c1 := SHORT(date DIV 100000);
d := c1 * 10;
date:= date MOD 100000;
str[0] := CHR(c1 + ORD('0')); (* '?x.??.?? xx:xx' *)
c1 := SHORT(date DIV 10000);
d := d + c1;
date:= date MOD 10000;
str[1] := CHR(c1 + ORD('0')); (* '??.??.?? xx:xx' *)
(* Stunde *)
c1 := SHORT(date DIV 1000);
date:= date MOD 1000;
str[9] := CHR(c1 + ORD('0')); (* '??.??.?? ?x:xx' *)
c1 := SHORT(date DIV 100);
date:= date MOD 100;
str[10] := CHR(c1 + ORD('0')); (* '??.??.?? ??:xx' *)
(* Minute *)
c1 := SHORT(date DIV 10);
date:= date MOD 10;
str[12] := CHR(c1 + ORD('0')); (* '??.??.?? ??:?x' *)
str[13] := CHR(SHORT(date) + ORD('0')); (* '??.??.?? ??:??' *)
str[14] := 0C;
InsertDay(y, m, d, str);
str[18 (*14*)] := 0C;
*)
END Long2DateStr;
(*
PROCEDURE NameToFile (REF name : ARRAY OF CHAR; REF path, fname : ARRAY OF CHAR):BOOLEAN;
(* wg. mtTextfiles.WriteLine der erste Parameter als VAR.. *)
(* 'name' in angegebene Datei schreiben, fr Absender- und Gruppennamen *)
VAR out : INTEGER;
BEGIN
out := CatFiles.OpenFile(path, fname, CatFiles.writeFile);
IF out > 0 THEN
CatFiles.Seek(0, out, CatFiles.end);
CatFiles.WriteMuch(LONG(LENGTH(name)), out, ADR(name));
CatFiles.WriteFile(CR, out); CatFiles.WriteFile(LF, out);
CatFiles.CloseFile(out);
RETURN CatFiles.FileError = 0
ELSE
MTE.info(MTE.nameNotWritten);
RETURN FALSE
END;
END NameToFile;
*)
(*--- Listen einlesen ---*)
PROCEDURE AppendToList(VAR list : Lists.List; REF str : ARRAY OF CHAR):BOOLEAN;
(* Ein Listenelement an die angegebene Liste anhngen; *
* 'spezielle' Stringlisten fr Gruppen- und Absendernamen *)
VAR new : listEntryPtr;
err : BOOLEAN;
l : CARDINAL;
BEGIN
l := LENGTH(str);
Storage.ALLOCATE(new, 6+1+l); (* CARDINAL, CARDINAL, BOOLEAN, String und 0C *)
err := new = NIL;
IF ~err THEN
WITH new^ DO
len := l;
selected := FALSE;
number := Lists.NoOfEntries(list)+1; (* wg. private *)
MagicStrings.Assign(str, gName); (* Mte nach der Anforderung jetzt gerade passen :-) *)
END;
Lists.AppendEntry(list, new, err);
(* !! Freigeben, wenn es nicht geklappt hat? *)
END;
RETURN ~err
END AppendToList;
TYPE whatsWrong = (nothingWrong, onlyFileNotFound, severeFault);
PROCEDURE ReadList (VAR list : Lists.List; REF fname : ARRAY OF CHAR):whatsWrong;
(* Eine der Namenlisten einlesen *)
VAR in : mtTextfiles.TEXTFILE;
err : BOOLEAN;
(*
new : listEntryPtr;
l : CARDINAL;
*)
scrap : String1023;
BEGIN
err := FALSE;
IF err THEN
MTE.InfoAlert(MTE.noFile1, fname, MTE.noFile2);
RETURN severeFault;
ELSIF mtTextfiles.OpenTextfile(fname, mtTextfiles.READ, 32768 , in)
THEN
WHILE ~err & ~mtTextfiles.EndofText(in) DO
mtTextfiles.ReadLine(in, scrap); mtTextfiles.ReadLn(in);
IF scrap[0] # 0C THEN
err := ~AppendToList(list, scrap); (* Fehler, falls es nicht geklappt hat *)
END;
END;
mtTextfiles.CloseTextfile(in);
MTE.noMemWarn(err);
ELSE
RETURN onlyFileNotFound
END;
(*
(* Testausgabe *)
InOut.WriteLn();
InOut.WriteString('--- data.Liste-----------------------');
Lists.ResetList(list);
REPEAT
new := Lists.NextEntry(list);
IF new # NIL THEN
InOut.WriteLn();
InOut.WriteString(new^.gName);
InOut.WriteCard(new^.number, 10);
END;
UNTIL new = NIL;
InOut.WriteLn();
InOut.WriteString('--- End of data.List -----------------');
*)
IF err THEN
RETURN severeFault;
ELSE
RETURN nothingWrong;
END;
END ReadList;
PROCEDURE ClearList (VAR l: Lists.List; killCarrier: BOOLEAN);
VAR entry: ADDRESS;
BEGIN
Lists.ResetList (l);
entry := Lists.PrevEntry (l);
WHILE entry # NIL DO
Lists.RemoveEntry (l, v.bool);
DEALLOCATE (entry, 0L);
entry := Lists.CurrentEntry (l);
END;
IF killCarrier THEN Lists.DeleteList (l, v.bool) END;
END ClearList;
PROCEDURE InitDataBase():BOOLEAN; (* exported *)
(* Initialisieren, Fehlermeldung wird selber ausgegeben *)
VAR err : BOOLEAN;
file: CatTypes.String255;
BEGIN
(* Erstmal beide Listen lschen, falls schon was drin ist *)
ClearList (names, FALSE);
MagicStrings.Assign (DataPath, file);
MagicStrings.Append (ADRInf, file);
err := ReadList (names, file) = severeFault;
err := err OR ~ReadPos();
ConfVars.GetConfDefBool(cDangerousDupeMode, dangerousDupeMode, TRUE);
ConfVars.GetConfDefBool(cUsenetChaining, usenetChaining, FALSE);
RETURN ~err ;
END InitDataBase;
PROCEDURE ResetDataBase();
BEGIN
END ResetDataBase;
PROCEDURE CloseBase():BOOLEAN; (* exported *)
(* Datenbank schlieen, abmelden am Ende *)
BEGIN
v.bool := WritePos();
RETURN TRUE
END CloseBase;
PROCEDURE PrepareToWrite();
(* setzt die Positionen so, da nach dem Einfgen etwas hbsches rauskommt :-) *)
VAR group : CARDINAL; ga : CARDINAL; (* Gruppen-Anzahl *)
BEGIN
FOR group := 0 TO grPos.posGroups-1 DO
(* hier ohne SetOnePos, da keine berschreitung der Grenze auftritt *)
grPos.pos^[group, neuePos] := empty;
END;
(* Als regelmige Aufrumaktion *)
END PrepareToWrite;
PROCEDURE Number2Name(nr : CARDINAL; VAR name : ARRAY OF CHAR);
(* Aus der Gruppennummer den Namensanfang (ohne Extension) erstellen *)
BEGIN
IF nr = private THEN
MagicStrings.Assign(rawPrivatename, name);
ELSE
DEC(nr);
(* Fr den Zugriff auf die Platte ist die Gruppennummer um eins zu verringern *)
MagicStrings.Assign(rawGroupname, name);
IF nr > 99 THEN
name[5] := CHR((nr DIV 100) + ORD('0'));
nr := nr MOD 100;
END;
name[6] := CHR((nr DIV 10) + ORD('0'));
name[7] := CHR((nr MOD 10) + ORD('0'));
END;
END Number2Name;
PROCEDURE FileLength(file : INTEGER):LONGCARD;
BEGIN
RETURN MagicDOS.Fseek(0, file, MagicDOS.SeekEnd); (*%%%%%*)
END FileLength;
PROCEDURE SearchNCountNew(handle : OneGroupHandle;
start : CARDINAL;
VAR pos,
count : CARDINAL;
searchOnly : BOOLEAN);FORWARD;
PROCEDURE unreadOk(handle : OneGroupHandle):BOOLEAN;FORWARD;
PROCEDURE forceUnreadRefresh(handle : OneGroupHandle); (* exported *)
(* erste Ungelesene suchen und durchzhlen *)
BEGIN
SearchNCountNew(handle, 0, grPos.pos^[handle^.group, unreadPos],
grPos.pos^[handle^.group, unreadCount], FALSE);
END forceUnreadRefresh;
PROCEDURE OpenOneGroup(group, add : CARDINAL; wannaWrite : BOOLEAN; VAR new : OneGroupHandle):BOOLEAN;(* exported *)
(* NIL, wenn nicht geklappt *)
(* add gibt an, fr wieviele neue Nachrichten Platz bentigt wird *)
VAR name : CatTypes.nameStrType;
open : CatTypes.nameStrType;
err : INTEGER;
avail,
alloc,
len : LONGCARD;
head : FileHeaderType;
PROCEDURE Abort();
(* Falls etwas schiefgeht, wenn alle Dateien offen sind *)
BEGIN
CatFiles.ErrorAlert(CatFiles.FileError);
CatFiles.CloseFile(new^.tabHandle);
CatFiles.CloseFile(new^.parHandle);
CatFiles.CloseFile(new^.datHandle);
DISPOSE(new);
END Abort;
PROCEDURE writeLog(REF open : ARRAY OF CHAR; error: INTEGER);
VAR errMsg: CatTypes.String255;
BEGIN
CatLog.WriteLine('- Fehler beim ffnen von Dateien -');
CatLog.WriteString('Kein Zugriff auf ');
CatLog.WriteString(open);
CatLog.WriteString(' -> Gemdos-Fehler #');
CatLog.WriteInt(error);
CatLog.WriteLn();
CatFiles.GetErrorMsg (error, errMsg);
CatLog.WriteString ('Fehlermeldung: ');
CatLog.WriteString (errMsg);
CatLog.WriteLn();
CatLog.WriteLine('-Ende der Durchsage -');
END writeLog;
BEGIN
NEW(new);
MTE.noMemWarn(new = NIL);
IF new # NIL THEN
new^.group := group;
Number2Name(group, name);
(* Tab-Datei ffnen *)
MagicStrings.Assign(name, open);
MagicStrings.Append(tabTag, open);
new^.tabHandle := CatFiles.OpenFile(DataPath, open, CatFiles.readWrite);
IF new^.tabHandle < 0 THEN
DISPOSE(new);
MTE.InfoAlert(MTE.noFile1, open, MTE.noFile2);
writeLog(open, CatFiles.FileError);
RETURN FALSE
END;
(* Par-Datei ffnen *)
MagicStrings.Assign(name, open);
MagicStrings.Append(parTag, open);
new^.parHandle := CatFiles.OpenFile(DataPath, open, CatFiles.readWrite);
IF new^.parHandle < 0 THEN
CatFiles.CloseFile(new^.tabHandle);
DISPOSE(new);
MTE.InfoAlert(MTE.noFile1, open, MTE.noFile2);
writeLog(open, CatFiles.FileError);
RETURN FALSE
ELSE
WITH new^ DO
len := FileLength(parHandle);
IF len = 0 THEN (* Neue Datei, erstmal den Header schreiben *)
CatFiles.Seek(0, parHandle, CatFiles.start);
CatFiles.WriteMuch(dbHeaderLength, parHandle, CADR(standardHeader));
anz := 0;
ELSE
CatFiles.Seek(0, parHandle, CatFiles.start);
CatFiles.ReadMuch(dbHeaderLength, parHandle, ADR(head));
IF isOldHeader(head) THEN
MTE.info(MTE.oldVersion);
RETURN FALSE;
END;
DEC(len, dbHeaderLength);
END;
anz := SHORT(len DIV LONG(TSIZE(pBlock)));
(* Anzahl an Nachrichten in der Gruppe *)
END;
IF CatFiles.FileError < 0 THEN
(* Dann ist beim Lesen des Kopfes oder beim Schreiben etwas schiefgelaufen *)
writeLog(open, CatFiles.FileError);
CatFiles.ErrorAlert(CatFiles.FileError);
CatFiles.CloseFile(new^.tabHandle);
CatFiles.CloseFile(new^.parHandle);
DISPOSE(new);
RETURN FALSE
END;
END;
(* Dat-Datei ffnen *)
MagicStrings.Assign(name, open);
MagicStrings.Append(datTag, open);
new^.datHandle := CatFiles.OpenFile(DataPath, open, CatFiles.readWrite);
IF new^.datHandle < 0 THEN
writeLog(open, CatFiles.FileError);
CatFiles.CloseFile(new^.tabHandle);
CatFiles.CloseFile(new^.parHandle);
DISPOSE(new);
MTE.InfoAlert(MTE.noFile1, open, MTE.noFile2);
RETURN FALSE
ELSE
new^.DatSize := FileLength (new^.datHandle);
CatFiles.Seek(0, new^.datHandle, CatFiles.start);
END;
WITH new^ DO
SetOnePos (group, letztePos, anz-1);
TabAnz := anz+add;
(* Allozierung nur, wenn genug Speicher frei ist *)
(* Wieviel ist frei *)
avail := MagicDOS.Malloc(LONG(-1));
(* Berechnen, was freibleiben soll *)
avail := avail - (TSIZE(CARDINAL) * LONG (TabAnz));
IF (TabAnz > 0)
& (avail > minDatBuffer)
THEN
tabBuff := MagicDOS.Malloc(TSIZE(CARDINAL)*LONG(TabAnz));
ELSE
tabBuff := NIL;
END;
avail := MagicDOS.Malloc(LONG(-1));
(* Berechnen, was freibleiben soll *)
avail := avail - (TSIZE(pBlock) * LONG (TabAnz));
IF (TabAnz > 0)
& (avail > minDatBuffer)
THEN
parBuff := MagicDOS.Malloc(TSIZE(pBlock)*LONG(TabAnz));
ELSE
parBuff := NIL;
END;
ParAnz := TabAnz; (* Das ist erstmal dasselbe *)
minTab := 0;
minPar := 0; (* More sophisticatet buffering in the future *)
IF (tabBuff # NIL) & (anz > 0) THEN (* und fllen.. *)
CatFiles.Seek(0, tabHandle, CatFiles.start);
CatFiles.ReadMuch(TSIZE(CARDINAL)*LONG(anz), tabHandle, tabBuff);
TabBuffAnz := anz;
IF CatFiles.FileError < 0 THEN
v.bool := MagicDOS.Mfree(parBuff);
v.bool := MagicDOS.Mfree(tabBuff);
err := CatFiles.FileError;
Abort();
writeLog('die crc-Tabelle', err);
RETURN FALSE;
END;
ELSE
TabBuffAnz := 0; (* In beiden ist erstmal nichts drin *)
END;
IF (parBuff # NIL) & (anz > 0) THEN (* und fllen.. *)
CatFiles.Seek(dbHeaderLength, parHandle, CatFiles.start);
CatFiles.ReadMuch(LONG(anz)*TSIZE(pBlock), parHandle, parBuff);
ParBuffAnz := anz;
IF CatFiles.FileError < 0 THEN
err := CatFiles.FileError;
v.bool := MagicDOS.Mfree(parBuff);
v.bool := MagicDOS.Mfree(tabBuff);
Abort();
writeLog('die Parameterdatei', err);
RETURN FALSE;
END;
ELSE
ParBuffAnz := 0; (* In beiden ist erstmal nichts drin *)
END;
IF wannaWrite THEN
IF tabBuff # NIL THEN
hash := Hashing2.ToHash(tabBuff, TabBuffAnz, add);
ELSE
hash := Hashing2.getEmptyHash()
END;
datBuff := NIL;
alloc := 0;
REPEAT
avail := MagicDOS.Malloc(LONG(-1));
IF avail >= minDatBuffer THEN
alloc := BinOps.LowerLCard(LONG(maxDatBuffer), avail);
datBuff := MagicDOS.Malloc(alloc);
END;
UNTIL (avail < minDatBuffer) OR (datBuff # NIL);
IF (avail < minDatBuffer)
& (datBuff # NIL)
THEN
v.bool := MagicDOS.Mfree(datBuff);
datBuff := NIL;
ELSE
DatAnz := BinOps.SwitchCard(datBuff # NIL, SHORT(alloc), 0);
DatBuffStart := MagicDOS.Fseek(0, datHandle, MagicDOS.SeekEnd);
DatBuffAnz := 0;
END;
IF GetOnePos (group, neuePos) = empty THEN
SetOnePos (group, neuePos, anz);
END; (* Das passiert also nur einmal pro Einfgen *)
ELSE (* IF wannaWrite *)
hash := Hashing2.getEmptyHash();
datBuff := NIL;
END;
(* Ntigenfalls einmal die ungelesenen Msgs durchzhlen und die erste suchen *)
IF parBuff # NIL
THEN
(* Wenn die PAR gebuffert ist, dann geht das so schnell, das knnen wir
* immer machen!
*)
forceUnreadRefresh(new);
ELSIF ~unreadOk(new) THEN
forceUnreadRefresh(new);
END;
CatLog.WriteStringNTime('Hashtabelle ');
IF Hashing2.emptyHash(hash) THEN CatLog.WriteString('nicht '); END;
CatLog.WriteString('angelegt ');
CatLog.WriteString('PAR ');
IF parBuff = NIL THEN CatLog.WriteString('nicht '); END;
CatLog.WriteString('gepuffert ');
CatLog.WriteString('TAB ');
IF tabBuff = NIL THEN CatLog.WriteString('nicht '); END;
CatLog.WriteString('gepuffert ');
CatLog.WriteString('DAT ');
IF datBuff = NIL THEN CatLog.WriteString('nicht '); END;
CatLog.WriteLine('gepuffert ');
writeThrough := TRUE;
open := TRUE;
touchlastpos := TRUE;
SetOnePos (group, letztePos, anz-1);
END; (* WITH new^ *)
(* und evtl. noch ein paar Checks/Fehlerkorrekturen, z.B. letzte Msg *)
RETURN TRUE
ELSE
RETURN FALSE
END;
END OpenOneGroup;
PROCEDURE CloseOneGroup(VAR handle : OneGroupHandle);
(* Gruppe, die zum Lesen geffnet war schlieen *)
BEGIN
CloseOneWriteGroup(handle, 0, TRUE, v.bool);
v.bool := WritePos();
END CloseOneGroup;
PROCEDURE CloseOneWriteGroup(VAR handle : OneGroupHandle;
newMsg : CARDINAL;
write : BOOLEAN;
VAR abort : BOOLEAN);
(* Gruppe, die zum schreiben geffnet war schlieen *)
(* newMsg -> Anzahl der neuen (d.h. nicht gefilterten oder ignorierten) Msgs *)
(* write -> Puffer wirklich zurckschreiben? Normalerweise nicht bei Abbruch *)
(* abort -> etwas ist beim Schlieen schiefgelaufen *)
PROCEDURE writeLog(abort : BOOLEAN; REF open : ARRAY OF CHAR);
VAR errMsg : CatTypes.String255;
BEGIN
IF abort THEN
CatLog.WriteLine('- Fehler beim Schlieen von Dateien -');
CatLog.WriteString('Kein Schreibzugriff auf ');
CatLog.WriteString(open);
CatLog.WriteString(' -> Gemdos-Fehler #');
CatLog.WriteInt(CatFiles.FileError);
CatLog.WriteLn();
CatFiles.GetErrorMsg (CatFiles.FileError, errMsg);
CatLog.WriteString ('Fehlermeldung: ');
CatLog.WriteString (errMsg);
CatLog.WriteLn();
CatLog.WriteLine('-Ende der Durchsage -');
CatFiles.ErrorAlert(CatFiles.FileError);
END;
END writeLog;
BEGIN
abort := FALSE;
WITH handle^ DO
SetOnePos (group, letztePos, anz-1);
IF (GetOnePos (group, unreadCount) = empty) OR
(GetOnePos (group, unreadPos) = empty) OR
(parBuff # NIL) THEN
forceUnreadRefresh(handle);
ELSE
(* Hier auch ohne SetOnePos, da auch hier keine Arrayberschreitung
* auftreten kann. Wenn es eine gab, dann wurde die schon vorher behoben
*)
INC(grPos.pos^[group, unreadCount], newMsg);
END;
(* Puffer freigeben.. umgekehrte Reihenfolge wie oben *)
IF datBuff # NIL THEN
IF write THEN
CatFiles.Seek(0, datHandle, CatFiles.end);
CatFiles.WriteMuch(LONG(DatBuffAnz), datHandle, datBuff);
abort := CatFiles.FileError < 0;
writeLog(abort, 'die Haupt-Datendatei');
END;
v.bool := MagicDOS.Mfree(datBuff);
END;
IF parBuff # NIL THEN
IF ~abort & write THEN
CatFiles.Seek(LONG(minPar)*LONG(TSIZE(pBlock))+dbHeaderLength, parHandle, CatFiles.start);
CatFiles.WriteMuch(LONG(ParBuffAnz)*TSIZE(pBlock), parHandle, parBuff);
abort := CatFiles.FileError < 0;
writeLog(abort, 'die Parameterdatei');
END;
v.bool := MagicDOS.Mfree(parBuff)
END;
IF tabBuff # NIL THEN
IF ~abort & write THEN
CatFiles.Seek(2*LONG(minTab), tabHandle, CatFiles.start);
CatFiles.WriteMuch(2*LONG(TabBuffAnz), tabHandle, tabBuff);
abort := CatFiles.FileError < 0;
writeLog(abort, 'die Crc-Tabelle');
END;
v.bool := MagicDOS.Mfree(tabBuff)
END;
(* Dateien schlieen *)
CatFiles.CloseFile(tabHandle);
CatFiles.CloseFile(parHandle);
CatFiles.CloseFile(datHandle);
Hashing2.ReleaseHash(hash);
END;
DISPOSE(handle);
handle := NIL;
END CloseOneWriteGroup;
PROCEDURE SetLogDate(VAR date : ARRAY OF CHAR); (* exported *)
(* Datum aus dem Logfile bernehmen *)
BEGIN
END SetLogDate;
PROCEDURE SaveNames ();
(* Sichert die Liste der Namen komplett
*)
VAR file : CatTypes.String255;
out : mtTextfiles.TEXTFILE;
num,
i : CARDINAL;
entry: listEntryPtr;
BEGIN
MagicStrings.Assign (DataPath, file);
MagicStrings.Append (ADRInf, file);
IF mtTextfiles.OpenTextfile (file, mtTextfiles.WRITE, 2048, out)
THEN
num := Lists.NoOfEntries (names);
FOR i := 1 TO num DO
Lists.ResetList (names);
entry := Lists.NextEntry (names);
WHILE (entry # NIL) & (entry^.number # i) DO
entry := Lists.NextEntry (names);
END;
IF entry # NIL
THEN
mtTextfiles.WriteLine (out, entry^.gName);
mtTextfiles.WriteLn(out);
END;
END;
mtTextfiles.CloseTextfile (out);
Protokoll.SendPathUpdate (DataPath);
END;
END SaveNames;
PROCEDURE AppendName(VAR name : ARRAY OF CHAR); (* exported *)
(* Neuen Absender bernehmen, meckert selber, falls es nicht klappt *)
BEGIN
MTE.noMemWarn(~AppendToList(names, name));
v.bool := GroupSelect.NameToFile(name, DataPath, ADRInf);
(* Jetzt eventuell noch sortieren *)
ConfVars.GetConfDefBool (cSortNames, v.bool, TRUE);
IF v.bool THEN
SortList (names);
END;
END AppendName;
PROCEDURE SortList (VAR l : Lists.List); (* exported *)
(* sortiert die Gruppenliste *)
VAR count : CARDINAL;
sort : POINTER TO ARRAY [0..$FFFF] OF ADDRESS;
i : CARDINAL;
adr : ADDRESS;
PROCEDURE grComp (a1, a2 : ADDRESS) : BOOLEAN;
VAR p1, p2 : POINTER TO listEntryPtr;
lp1, lp2: listEntryPtr;
str1, str2 : CatTypes.String255;
BEGIN
p1 := a1;
p2 := a2;
lp1 := p1^;
lp2 := p2^;
MagicStrings.Assign (lp1^.gName, str1);
MagicStrings.Assign (lp2^.gName, str2);
MagicStrings.CAPS (str1);
MagicStrings.CAPS (str2);
RETURN MagicStrings.Compare (str1, str2) = MagicStrings.less;
END grComp;
PROCEDURE grComp2 (a1, a2 : ADDRESS) : BOOLEAN;
VAR p1, p2 : POINTER TO listEntryPtr;
lp1, lp2: listEntryPtr;
str1, str2 : CatTypes.String255;
BEGIN
lp1 := a1;
lp2 := a2;
MagicStrings.Assign (lp1^.gName, str1);
MagicStrings.Assign (lp2^.gName, str2);
MagicStrings.CAPS (str1);
MagicStrings.CAPS (str2);
MagicDOS.Cconws (12c+15c+0c);
MagicDOS.Cconws (str1);
MagicDOS.Cconws (12c+15c+0c);
MagicDOS.Cconws (str2);
MagicDOS.Cconws (12c+15c+0c);
RETURN MagicStrings.Compare (str1, str2) = MagicStrings.less;
END grComp2;
BEGIN
(*
v.bool := QuickSort.ListSort (l, grComp2, testBreak);
*)
count := Lists.NoOfEntries (l);
IF count = 0 THEN RETURN END;
ALLOCATE (sort, LONG(count) * TSIZE (ADDRESS));
IF sort = NIL THEN RETURN END;
Lists.ResetList (l);
FOR i := 0 TO count-1 DO
sort^[i] := Lists.NextEntry (l);
END;
v.bool := QuickSort.sortIt (0, count-1, sort^, grComp, TSIZE (ADDRESS), QuickSort.noBreak);
(* Liste wieder zurckbernehmen *)
Lists.ResetList (l);
FOR i := 0 TO count-1 DO
adr := Lists.NextEntry (l);
Lists.RemoveEntry (l, v.bool);
END;
Lists.ResetList (l);
FOR i := 0 TO count - 1 DO
Lists.AppendEntry (l, sort^[i], v.bool);
END;
DEALLOCATE (sort, 0);
END SortList;
(*------------------------------------------------------------------------*)
PROCEDURE ReadPos():BOOLEAN; (* exported *)
VAR handle : INTEGER; z : CARDINAL; z2 : posType; ready : BOOLEAN;
count : CARDINAL;
size : LONGCARD;
PROCEDURE exist(REF path, name : ARRAY OF CHAR):BOOLEAN;
VAR file : CatTypes.String255;
exists: BOOLEAN;
BEGIN
MagicStrings.Assign(path, file);
MagicStrings.Append(name, file);
RETURN CatFiles.Exists(file)
END exist;
BEGIN
(* Erstmal alten Kram deallozieren *)
IF grPos.pos # NIL
THEN
DEALLOCATE (grPos.pos, 0);
END;
handle := CatFiles.OpenFile(DataPath, GRPos, CatFiles.readFile);
IF handle > 0 THEN
CatFiles.ReadMuch(dataSys.dbHeaderLength, handle, ADR(grPos.head));
ready := CatFiles.FileError >= 0;
ELSE
ready := FALSE;
END;
IF ready
THEN
(* Jetzt prfen, welchen Header wir haben *)
IF (grPos.head.CatMagic # dbCatMagic) OR
(grPos.head.Version # dbVersion) OR
((grPos.head.VersionMagic # dbVersionMagic) &
(grPos.head.VersionMagic # dataSys.grPosVersionMagic))
THEN
MTE.info (MTE.oldVersion);
CatFiles.CloseFile(handle);
RETURN FALSE
END;
IF grPos.head.VersionMagic # dataSys.grPosVersionMagic
THEN
(* Altes Gruppen.POS, konvertieren *)
(* wieviele Gruppen haben wir denn? *)
grPos.usedGroups := GroupSelect.CountCatGroups();
IF ~CreatePosArray (grPos.usedGroups)
THEN
MTE.noMemAlert();
CatFiles.CloseFile(handle);
RETURN FALSE
END;
(* Und jetzt einlesen *)
CatFiles.ReadMuch(LONG(grPos.usedGroups) * TSIZE(dataSys.onePos), handle, grPos.pos);
(* Savebereich fr Datum noch lschen *)
Block.Clear (ADR(grPos.save), TSIZE (dataSys.onePos));
ELSE
(* Jetzt mal sehen, wieviele Gruppen darin gespeichert sind *)
(* Gre feststellen *)
CatFiles.Seek (0, handle, CatFiles.end);
size := CatFiles.FilePos (handle);
CatFiles.Seek (0, handle, CatFiles.start);
count := SHORT((size-dataSys.dbHeaderLength) DIV TSIZE (dataSys.onePos));
DEC (count); (* Wegen save-Info *)
CatFiles.Seek (dataSys.dbHeaderLength, handle, CatFiles.start);
CatFiles.ReadMuch(TSIZE (dataSys.onePos), handle, ADR(grPos.save));
ready := ready & (CatFiles.FileError >= 0);
(* Jetzt Speicher allozieren *)
IF ~CreatePosArray (count)
THEN
MTE.noMemAlert();
CatFiles.CloseFile(handle);
RETURN FALSE
END;
CatFiles.ReadMuch(LONG(count) * TSIZE (dataSys.onePos), handle, grPos.pos);
ready := ready & (CatFiles.FileError >= 0);
END;
CatFiles.CloseFile(handle);
MTE.warnAlert(~ready, MTE.noGrPos, '', '');
ELSE
ready := TRUE;
(* Mal sehen, ob der Typ Dateien hat.. *)
IF exist(DataPath, 'grupp???.par') OR
exist(DataPath, 'grupp???.dat') OR
exist(DataPath, 'grupp???.tab') OR
exist(DataPath, 'private.par') OR
exist(DataPath, 'private.par') OR
exist(DataPath, 'private.dat') OR
exist(DataPath, 'private.tab')
THEN
(* ja, er hat eine, dann werden wir ihn mal interviewen *)
IF mtAlerts.Alert(2, MTE.possibleOldVersion) = 2 THEN RETURN FALSE END;
END;
(* Jetzt Default anlegen *)
count := GroupSelect.CountCatGroups();
IF ~CreatePosArray (count)
THEN
MTE.noMemAlert();
RETURN FALSE
END;
FOR z := 0 TO grPos.posGroups - 1 DO
FOR z2 := aktuellePos TO unreadCount DO
grPos.pos^[z, z2] := empty
END;
END;
grPos.head := standardHeader;
END;
grPosRead := ready; (* globale Variable fr WritePos *)
RETURN grPosRead;
END ReadPos;
PROCEDURE WritePos():BOOLEAN; (* exported *)
(* Positionstabelle einlesen und schreiben *)
VAR handle : INTEGER;
BEGIN
IF grPosRead THEN
handle := CatFiles.CreateFile(DataPath, GRPos);
IF handle > 0 THEN
grPos.head := standardHeader;
grPos.head.VersionMagic := dataSys.grPosVersionMagic;
(* Header schreiben *)
CatFiles.WriteMuch(dataSys.dbHeaderLength, handle, ADR(grPos.head));
(* Save-Bereich schreiben *)
CatFiles.WriteMuch(TSIZE(dataSys.onePos), handle, ADR(grPos.save));
(* Gruppenbereich schreiben *)
CatFiles.WriteMuch(LONG (grPos.usedGroups) * TSIZE(dataSys.onePos), handle, grPos.pos);
CatFiles.CloseFile(handle);
END;
IF (handle < 0) OR (CatFiles.FileError < 0) THEN
MTE.info(MTE.postableNotWritten);
RETURN FALSE;
ELSE
RETURN TRUE
END;
ELSE
RETURN TRUE; (* wurde garnicht gelesen! *)
END;
END WritePos;
(*--- Schutzfunktionen ---*)
PROCEDURE GetCheckArea (VAR area: dataSys.onePos);
(* Holt die Prfarea fr das Datum aus dem GRUPPEN.POS
*)
BEGIN
area := grPos.save;
END GetCheckArea;
PROCEDURE SetCheckArea (area: dataSys.onePos);
(* Setzt die Prfarea fr das Datum in dem GRUPPEN.POS
*)
BEGIN
grPos.save := area;
END SetCheckArea;
(*--- Positionsfunktionen ---*)
PROCEDURE FirstNewMsg(group : CARDINAL):CARDINAL; (* exported *)
(* erste Msg aus dem letzten Outfile in dieser Gruppe *)
BEGIN
RETURN GetOnePos (group, neuePos);
END FirstNewMsg;
PROCEDURE LastMsgOfGroup(group : CARDINAL):CARDINAL; (* exported *)
(* letzte Msg der Gruppe *)
BEGIN
RETURN GetOnePos (group, letztePos);
END LastMsgOfGroup;
PROCEDURE lastReadMsgOfGroup(group : CARDINAL):CARDINAL; (* exported *)
(* letzte vom User gelesene Msg dieser Gruppe *)
BEGIN
RETURN GetOnePos (group, aktuellePos);
END lastReadMsgOfGroup;
PROCEDURE unreadMsgCount(group : CARDINAL):CARDINAL;
(* Anzahl der ungelesenen Msgs in der Gruppe *)
BEGIN
RETURN GetOnePos (group, unreadCount);
END unreadMsgCount;
PROCEDURE unreadMsgPos(group : CARDINAL):CARDINAL;
(* Position der ersten ungelesenen Msg in der Gruppe *)
BEGIN
RETURN GetOnePos (group, unreadPos);
END unreadMsgPos;
PROCEDURE SetLastReadMsg(group, nr : CARDINAL);
(* neue letzte vom User gelesene Msg setzen *)
BEGIN
SetOnePos (group, aktuellePos, nr);
END SetLastReadMsg;
(*--- Zugriffe auf die Datenbank ---*)
PROCEDURE ReadBlock(handle : OneGroupHandle;
nr : CARDINAL;
VAR ptr : pBlockPtr):errorType;
(* Fr evtl. Pufferung, die ungewhnliche Deklaration, kein unntiges umkopieren *)
BEGIN
WITH handle^ DO
IF nr < anz THEN (* echt kleiner! Siehe z.B. eine drin und Nummer 0 lesen *)
IF (parBuff # NIL) & (nr >= minPar) & (nr-minPar < ParBuffAnz) THEN
ptr := ADR(parBuff^[nr-minPar]); (* Hh, guter Einfall, nicht? *)
CatFiles.FileError := 0; (* kein Fehler aufgetreten! *)
ELSE
CatFiles.Seek(LONG(nr)*LONG(TSIZE(pBlock))+dbHeaderLength, parHandle, CatFiles.start);
CatFiles.ReadMuch(TSIZE(pBlock), parHandle, ptr);
END;
IF CatFiles.FileError < 0 THEN
RETURN fileError
ELSE
RETURN noError
END;
ELSE
RETURN notFound
END;
END; (* WITH handle^ DO *)
END ReadBlock;
PROCEDURE ReadBlockCrc(handle : OneGroupHandle;
nr : CARDINAL;
VAR ptr : pBlockPtr):errorType;
(* Block mit crc-Check einlesen *)
VAR err : errorType;
compareCrc : CARDINAL;
BEGIN
err := ReadBlock(handle, nr, ptr);
IF err = noError THEN
compareCrc := CalcCrcArray(ptr+ADDRESS(2), SHORT(TSIZE(pBlock))-2);
IF compareCrc # ptr^.crc THEN
CatFiles.FileError := CatFiles.crcError;
RETURN crcError
ELSE
RETURN noError
END;
ELSE
RETURN err
END;
END ReadBlockCrc;
PROCEDURE WriteBlock(handle : OneGroupHandle;
nr : CARDINAL;
ptr : pBlockPtr); (* wg. Compiler *)
(* Fehlerbehandlung..!!! *)
(* Hier auch einen Pointer bergeben, bei Pufferung einfach nichts machen, wenn *)
(* der Zeiger schon richtig steht. Die anderen Prozeduren arbeiten im Normalfall *)
(* mit einem Block, dessen Zeiger sie vielleicht schon von ReadBlock bekommen haben *)
BEGIN
(* Debug: Fehler provozieren IF stopSearch() THEN error := fileError; CatFiles.FileError := -1; RETURN END; *)
(* writeThrough? *)
error := noError;
WITH handle^ DO
IF nr < anz THEN (* echt kleiner! Siehe z.B. eine drin und Nummer 0 lesen *)
IF (parBuff # NIL) & (nr >= minPar) & (nr-minPar < ParBuffAnz) THEN
IF ptr # ADR(parBuff^[nr-minPar]) THEN
parBuff^[nr-minPar] := ptr^; (* Dann noch zuweisen *)
END;
ELSE
CatFiles.Seek(LONG(nr)*LONG(TSIZE(pBlock))+dbHeaderLength, parHandle, CatFiles.start);
CatFiles.WriteMuch(TSIZE(pBlock), parHandle, ptr);
IF CatFiles.FileError < 0 THEN
error := fileError
END;
END;
ELSE
HALT; (* Das sollte nmlich nicht auftreten.. *)
END;
END; (* WITH handle^ DO *)
END WriteBlock;
PROCEDURE WriteBlockCrc(handle : OneGroupHandle;
nr : CARDINAL;
ptr : pBlockPtr); (* wg. Compiler *)
BEGIN
ptr^.crc := CalcCrcArray(ptr+ADDRESS(2), SHORT(TSIZE(pBlock))-2);
WriteBlock(handle, nr, ptr);
END WriteBlockCrc;
PROCEDURE AppendBlock(handle : OneGroupHandle; VAR block : pBlock);
(* Fehlerbehandlung!! *)
(* Einen Block an PAR anhngen *)
BEGIN
(* Debug: Fehler provozieren IF stopSearch() THEN error := fileError; CatFiles.FileError := -1; RETURN END; *)
error := noError;
WITH handle^ DO
IF (parBuff # NIL) & (anz < minPar + ParAnz) & (ParBuffAnz < ParAnz) THEN
parBuff^[anz-minPar] := block;
INC(ParBuffAnz);
ELSE
CatFiles.Seek(0, parHandle, CatFiles.end);
CatFiles.WriteMuch(TSIZE(pBlock), parHandle, ADR(block));
IF CatFiles.FileError < 0 THEN
error := fileError
END;
END;
INC(anz); (* Jetzt haben wir einen mehr *)
END; (* WITH handle^ DO *)
END AppendBlock;
PROCEDURE GetCrc(handle : OneGroupHandle;
nr : CARDINAL):CARDINAL;
VAR z : CARDINAL;
BEGIN
error := noError;
WITH handle^ DO
IF nr < anz THEN (* echt kleiner! Siehe z.B. eine drin und Nummer 0 lesen *)
IF (tabBuff # NIL) & (nr >= minTab) & (nr-minTab < TabBuffAnz) THEN
z := tabBuff^[nr-minTab];
ELSE
CatFiles.Seek(LONG(nr) * 2, tabHandle, CatFiles.start);
CatFiles.ReadMuch(2, tabHandle, ADR(z));
IF CatFiles.FileError < 0 THEN
error := fileError
END;
END;
ELSE
HALT; (* Sollte nicht auftreten *)
END;
END; (* WITH handle^ DO *)
RETURN z
END GetCrc;
PROCEDURE AppendCrc(handle : OneGroupHandle; crc : CARDINAL);
(* Eine Crc an TAB anhngen *)
BEGIN
error := noError;
WITH handle^ DO
IF (tabBuff # NIL) & (anz < minTab + TabAnz) & (TabBuffAnz < TabAnz) THEN
tabBuff^[anz-minTab] := crc;
INC(TabBuffAnz);
Hashing2.AddCrc(hash, crc);
ELSE
CatFiles.Seek(0, tabHandle, CatFiles.end);
CatFiles.WriteMuch(2, tabHandle, ADR(crc));
IF CatFiles.FileError < 0 THEN
error := fileError
END;
END;
END; (* WITH handle^ DO *)
END AppendCrc;
PROCEDURE ReadFromDat(handle : OneGroupHandle;
start,
len : LONGCARD;
strAdr : ADDRESS);
BEGIN
error := noError;
WITH handle^ DO
IF (datBuff # NIL) &
(start >= DatBuffStart) & (start+len <= DatBuffStart+LONG(DatBuffAnz)) THEN
Block.Copy(ADR(datBuff^[SHORT(start-DatBuffStart)]), len, strAdr);
ELSE
IF start <= DatSize
THEN
CatFiles.Seek(start, datHandle, CatFiles.start);
CatFiles.ReadMuch(len, datHandle, strAdr);
IF CatFiles.FileError < 0 THEN
error := fileError
END;
ELSE
error := fileError
END;
END;
END; (* WITH handle^ DO *)
END ReadFromDat;
PROCEDURE WriteToDat(handle : OneGroupHandle;
start,
len : LONGCARD;
strAdr : ADDRESS);
BEGIN
WITH handle^ DO
error := noError;
CatFiles.Seek(start, datHandle, CatFiles.start);
CatFiles.WriteMuch(len, datHandle, strAdr);
IF CatFiles.FileError < 0 THEN
error := fileError
END;
END; (* WITH handle^ DO *)
END WriteToDat;
PROCEDURE AppendToDat(handle : OneGroupHandle;
adr : ADDRESS;
howMuch : LONGCARD;
VAR startOffs : LONGCARD);
(* Daten an die DAT anhngen *)
BEGIN
(* Debug: Fehler provozieren IF stopSearch() THEN error := fileError; CatFiles.FileError := -1; RETURN END; *)
error := noError;
WITH handle^ DO
IF datBuff # NIL THEN
IF LONG(DatBuffAnz) + howMuch > LONG(DatAnz) THEN (* Kein Platz mehr *)
CatFiles.Seek(0, datHandle, CatFiles.end);
CatFiles.WriteMuch(LONG(DatBuffAnz), datHandle, datBuff);
INC (DatSize, DatBuffAnz);
(* Erstmal den Puffer leeren *)
DatBuffAnz := 0;
DatBuffStart := MagicDOS.Fseek(0, datHandle, MagicDOS.SeekEnd);
END;
IF howMuch > LONG(DatAnz) THEN
(* Dann ist in jedem Fall auch die obige IF-Bedingung erfllt gewesen! *)
startOffs := DatBuffStart;
CatFiles.WriteMuch(howMuch, datHandle, adr);
INC (DatSize, howMuch);
ELSE
startOffs := DatBuffStart + LONG(DatBuffAnz);
Block.Copy(adr, howMuch, ADR(datBuff^[DatBuffAnz]));
INC(DatBuffAnz, SHORT(howMuch));
END;
ELSE
startOffs := MagicDOS.Fseek(0, datHandle, MagicDOS.SeekEnd);
CatFiles.WriteMuch(howMuch, datHandle, adr);
INC (DatSize, howMuch);
END;
END; (* WITH handle^ DO *)
IF CatFiles.FileError < 0 THEN
error := fileError
END;
END AppendToDat;
PROCEDURE ReadID(handle : OneGroupHandle;
IDStart : LONGCARD;
count : CARDINAL;
VAR id : ARRAY OF CHAR);
BEGIN
ReadFromDat(handle, IDStart, LONG(count), ADR(id));
END ReadID;
PROCEDURE ReadOtherID(handle : OneGroupHandle;
nr : CARDINAL;
VAR id : ARRAY OF CHAR;
VAR isOldDupe : BOOLEAN);
VAR s : pBlock; blockPtr : pBlockPtr;
BEGIN
blockPtr := ADR(s);
IF ReadBlock(handle, nr, blockPtr) = noError THEN
ReadID(handle, blockPtr^.Start, blockPtr^.idLength, id);
isOldDupe := dangerousDupeMode & (bOldDupe IN blockPtr^.bits);
ELSE
MagicStrings.Assign(IDError, id);
isOldDupe := FALSE;
END;
END ReadOtherID;
PROCEDURE ReadOtherRId(handle : OneGroupHandle;
nr : CARDINAL;
VAR id : ARRAY OF CHAR;
VAR isOldDupe : BOOLEAN):BOOLEAN;
VAR s : pBlock;
blockPtr : pBlockPtr;
InfoStrings : CatTypes.BigTextPtr;
pos : POINTER TO ARRAY[0..1000] OF CARDINAL;
z : CARDINAL;
lauf : CARDINAL;
ptr : CatTypes.Str255Ptr;
BEGIN
isOldDupe := FALSE;
blockPtr := ADR(s);
IF ReadBlock(handle, nr, blockPtr) = noError THEN
WITH blockPtr^ DO
IF ~(mMId IN items) THEN RETURN FALSE END;
Storage.ALLOCATE(InfoStrings, LONG(hLength));
IF InfoStrings # NIL THEN
ReadFromDat(handle, Start, LONG(hLength), InfoStrings);
pos := ADDRESS(InfoStrings) + ADDRESS(LONG(idLength));
IF ODD(idLength) THEN INC(pos) END;
z := 2;
(* Reply-ID ist da in diesem Fall! *)
FOR lauf := mVon TO mMId DO
IF lauf IN items THEN INC(z) END;
END;
ptr := ADDRESS(InfoStrings) + ADDRESS(LONG(pos^[z-1]));
MagicStrings.Assign(ptr^, id);
Storage.DEALLOCATE(InfoStrings, 0L);
ELSE
RETURN FALSE;
END;
END;
isOldDupe := dangerousDupeMode & (bOldDupe IN blockPtr^.bits);
ELSE
RETURN FALSE;
END;
RETURN TRUE;
END ReadOtherRId;
PROCEDURE SearchNCountNew(handle : OneGroupHandle;
start : CARDINAL;
VAR pos,
count : CARDINAL;
searchOnly : BOOLEAN);
(* Datenbank nach der ersten ungelesenen durchsuchen und die ungelesenen durchzhlen *)
VAR s : pBlock; blockPtr : pBlockPtr; found : BOOLEAN;
BEGIN
blockPtr := ADR(s); found := FALSE;
IF ~searchOnly THEN count := 0 END;
WHILE (start <= handle^.anz) & (ReadBlock(handle, start, blockPtr) = noError) DO
IF ~(bGelesen IN blockPtr^.bits) THEN
IF ~found THEN
pos := start; (* Hier ist die erste ungelesene.. *)
found := TRUE;
END;
IF searchOnly THEN RETURN END; (* alles klar, wir haben einen gefunden.. *)
INC(count);
END;
INC(start);
blockPtr := ADR(s);
END;
END SearchNCountNew;
PROCEDURE unreadOk(handle : OneGroupHandle):BOOLEAN;
(* "erste ungelesene" berprfen, ob sie wirklich ungelesen ist *)
VAR s : pBlock; blockPtr : pBlockPtr;
BEGIN
IF (GetOnePos (handle^.group, unreadPos) = empty) OR
(GetOnePos (handle^.group, unreadPos) >= handle^.anz) OR
(GetOnePos (handle^.group, unreadCount) = empty) OR
(GetOnePos (handle^.group, unreadCount) >= handle^.anz) THEN
RETURN FALSE
END;
blockPtr := ADR(s);
RETURN (ReadBlock(handle, GetOnePos (handle^.group, unreadPos), blockPtr) = noError) &
~(bGelesen IN blockPtr^.bits);
END unreadOk;
(*--- bestimmte Sachen suchen ---*)
PROCEDURE SearchID(handle : OneGroupHandle;
REF searchID : ARRAY OF CHAR;
start : CARDINAL;
rev : BOOLEAN;
ignoreOldDupe : BOOLEAN; (* sollen alte Dupes bersprungen werden? *)
(* Normalerweise FALSE *)
VAR nr : CARDINAL):BOOLEAN;
(* Die Nummer der Nachricht mit der bergebenen ID herausfinden *)
VAR compareCrc : CARDINAL;
PROCEDURE IDOkay():BOOLEAN;
VAR scrapID : String1023; isOldDupe : BOOLEAN;
BEGIN
ReadOtherID(handle, nr, scrapID, isOldDupe);
(*
RETURN MagicStrings.Equal(scrapID, searchID) & (~ignoreOldDupe OR ~isOldDupe)
*)
RETURN AssFuncs.CmpId (scrapID, searchID) & (~ignoreOldDupe OR ~isOldDupe)
END IDOkay;
(* Erluterung: Die letzten beiden werden nur verwendet, wenn man im *)
(* DangerousDupeMode ist. Ansonsten ist isOldDupe _immer_ FALSE, es *)
(* passiert also nix *)
BEGIN
IF rev
THEN IF handle^.anz = 0 THEN RETURN FALSE ELSE nr := handle^.anz-1 END;
ELSE nr := start
END;
compareCrc := CalcIdCrc (searchID);
IF Hashing2.emptyHash(handle^.hash) THEN
WHILE ( (~rev & (nr < handle^.anz)) OR
( rev & (nr < CARDINAL(-1) )) ) & (* Das funktioniert *)
(CatFiles.FileError >= 0) DO
IF (GetCrc(handle, nr) = compareCrc) & IDOkay() & (CatFiles.FileError >= 0) THEN RETURN TRUE END;
IF rev THEN DEC(nr) ELSE INC(nr) END
END;
ELSE
nr := Hashing2.GetFirst(handle^.hash, compareCrc);
WHILE nr # empty DO
IF IDOkay() & (CatFiles.FileError >= 0) THEN RETURN TRUE END;
nr := Hashing2.GetNext(handle^.hash);
END;
END;
RETURN FALSE;
END SearchID;
(* Fehlerrckgabe der nchsten 2 Positionsfunktionen: noError/notFound *)
PROCEDURE NumberOfID(handle : OneGroupHandle;
REF ID : ARRAY OF CHAR):CARDINAL; (* exported *)
(* MsgNummer zu einer ID feststellen *)
VAR nr : CARDINAL;
BEGIN
IF ID[0] = 0C THEN
error := notFound;
RETURN empty;
ELSIF SearchID(handle, ID, 0, FALSE, TRUE, nr) THEN (* Neueste mit dieser ID finden.. *)
error := noError;
RETURN nr;
ELSE
error := notFound;
RETURN empty;
END;
END NumberOfID;
PROCEDURE NumberOfDate(handle : OneGroupHandle;
date : LONGCARD):CARDINAL; (* exported *)
VAR s : pBlock; blockPtr : pBlockPtr; nr : CARDINAL; ok : BOOLEAN;
BEGIN
nr := 0;
REPEAT
blockPtr := ADR(s);
ok := ReadBlock(handle, nr, blockPtr) = noError;
INC(nr);
UNTIL ~ok OR (nr >= handle^.anz) OR (blockPtr^.Datum >= date);
RETURN nr-1
(* d.h. evtl. wird hier genau der defekte Block zurckgegeben *)
(* crc-error notfalls selber behandeln! *)
END NumberOfDate;
(*--- Hilfprozeduren zum lesen ---*)
PROCEDURE iReadHeader(handle : OneGroupHandle;
nr : CARDINAL;
blockPtr : pBlockPtr;
VAR mess : MessageType);
(* Fllt mess mit dem, was man aus dem ParameterBlock bekommt,
* Eigentlich sind keine Fehler mglich.. :-)
*)
VAR compareCrc : CARDINAL;
BEGIN
compareCrc := CalcCrcArray(blockPtr+ADDRESS(2), SHORT(TSIZE(pBlock))-2);
IF compareCrc # blockPtr^.crc THEN
error := crcError;
MTE.info(MTE.badMsg);
RETURN;
END;
Block.Clear (ADR(mess), TSIZE (MessageType));
WITH mess DO
MailNr := nr;
MailAnz := handle^.anz;
MailID := ADR(emptyString); (* noch nicht bekannt *)
KommentierteID := ''; (* noch nicht bekannt *)
fromOther := FALSE; (* auch unbekannt *)
Betreff := ADR(emptyString); (* Kommt auch aus der *.dat *)
Absender := ADR(emptyString); (* noch nicht bekannt *)
Gruppe := handle^.group;
Long2DateStr(blockPtr^.Datum, Datum);
EigeneNachricht := FALSE;
StatusDatum := '';
left := blockPtr^.leftMess;
right := blockPtr^.rightMess;
up := blockPtr^.upMess;
down := blockPtr^.downMess;
KommentarAnzahl := blockPtr^.KomCount;
tauschDate := blockPtr^.Datum;
infoLen := blockPtr^.hLength;
textLen := blockPtr^.Length;
StatusBits := blockPtr^.bits;
Status := 'N';
statusDate := 0;
Text := NIL;
InfoStrings := NIL;
distribution := dNone;
END;
END iReadHeader;
PROCEDURE iReadRest(handle : OneGroupHandle;
blockPtr : pBlockPtr;
readText : BOOLEAN; (* auch Messagetext einlesen? *)
VAR mess : MessageType);
(* Restliche Daten lesen, nur falls bisher alles gut gelesen wurde,
* d.h. error = noError
*)
VAR pos : POINTER TO ARRAY[0..1000] OF CARDINAL;
PROCEDURE lauf2Ptr(lauf : CARDINAL; VAR z : CARDINAL);
VAR ptr : CatTypes.Str255Ptr; (* ADDRESS, nur fr mRefNr *)
BEGIN
IF lauf IN blockPtr^.items THEN
ptr := ADDRESS(mess.InfoStrings) + ADDRESS(LONG(pos^[z]));
INC(z);
ELSE
ptr := ADR(emptyString);
END;
CASE lauf OF
mVon : mess.Absender := ptr;
| mAn : mess.Empfaenger := ptr;
| mMId : mess.mid := ptr;
| mRId : mess.rid := ptr;
| mBox : mess.box := ptr;
| mName : mess.name := ptr;
| mGate : mess.gate := ptr;
| mMime : mess.mime := ptr;
| mFollowup : mess.followupTo := ptr;
| mReplyTo : mess.replyTo := ptr;
| mSender : mess.sender := ptr;
| mRefNr: MagicStrings.Assign(ptr^, mess.KommentierteID);
| mDistribution : IF ptr # NIL THEN
CASE ptr^[0] OF
'N' : mess.distribution := dNet;
| 'M' : mess.distribution := dMausNet;
| 'L' : mess.distribution := dLokal;
| ELSE mess.distribution := dNone;
END;
ELSE
mess.distribution := dNone;
END;
ELSE
HALT
END;
END lauf2Ptr;
VAR lauf,
z : CARDINAL;
p : pInfoPtr;
cp : POINTER TO ARRAY[0..SHORT(TSIZE(pInfoType))] OF BYTE;
cz : CARDINAL;
BEGIN
IF error # noError THEN RETURN END;
WITH blockPtr^ DO
Storage.ALLOCATE(mess.InfoStrings, LONG(hLength));
IF mess.InfoStrings # NIL THEN
ReadFromDat(handle, Start, LONG(hLength), mess.InfoStrings);
mess.MailID := ADDRESS(mess.InfoStrings);
pos := ADDRESS(mess.InfoStrings) + ADDRESS(LONG(idLength));
IF ODD(idLength) THEN INC(pos) END;
mess.Betreff := ADDRESS(mess.InfoStrings)+ADDRESS(LONG(pos^[1]));
z := 2;
FOR lauf := mVon TO mSender DO
lauf2Ptr(lauf, z);
END;
IF mPrivateBytes IN items THEN
p := mess.InfoStrings + ADDRESS(LONG(hLength) - TSIZE(pInfoType));
IF ODD(LONGCARD(p)) THEN
(* Speziell fr Datenbanken auf 68000ern, die nicht aligned sind *)
(* Davon gibt's nur eine, die von meinem Bruder! *)
cp := ADDRESS(p);
FOR cz := SHORT(TSIZE(pInfoType)) TO 1 BY -1 DO
cp^[cz] := cp^[cz-1];
END;
INC(p);
END;
Long2DateStr(p^.LeseDatum, mess.StatusDatum);
mess.EigeneNachricht := p^.locked # 0C;
mess.Status := p^.Status;
mess.statusDate := p^.LeseDatum;
END;
ELSE
error := noMemErr;
RETURN
END;
(*-- Ist ja jetzt im Header und wird normalerweise von dort geladen! --*)
IF (error = noError) & (blockPtr^.upMess # empty) & (blockPtr^.upMess # notSaved)
& (blockPtr^.upMess < handle^.anz) & ~(mRefNr IN blockPtr^.items) THEN
ReadOtherID(handle, blockPtr^.upMess, mess.KommentierteID, v.bool);
(* Nur in diesem Fall von der kommentierten Msg besorgen! *)
IF error = noError THEN
mess.fromOther := TRUE;
ELSE
mess.fromOther := FALSE;
mess.KommentierteID[0] := 0C;
END;
error := noError;
END;
(*---------*)
IF readText THEN
Storage.ALLOCATE(mess.Text, LONG(Length)+editSecureBytes);
IF mess.Text # NIL THEN
ReadFromDat(handle, Start+LONG(hLength), LONG(Length), mess.Text);
ELSE
DEALLOCATE(mess.InfoStrings, 0);
error := noMemErr;
END;
ELSE
END;
END;
END iReadRest;
(*--- Leseprozeduren ---*)
PROCEDURE ReadState(handle : OneGroupHandle; nr : CARDINAL; VAR flags : BITSET);
(* Nur die Flags lesen, fr "Gelesene ignorieren" *)
VAR s : pBlock; blockPtr : pBlockPtr;
BEGIN
blockPtr := ADR(s);
IF ReadBlockCrc(handle, nr, blockPtr) = noError THEN
flags := blockPtr^.bits;
ELSE
flags := {};
END;
END ReadState;
PROCEDURE ReadPersState(handle : OneGroupHandle; nr : CARDINAL; VAR state : CHAR; VAR own : BOOLEAN; VAR bits : BITSET);
(* Nur den Status einer persnlichen Msg lesen *)
VAR s : pBlock; blockPtr : pBlockPtr;
pers : pInfoType;
BEGIN
blockPtr := ADR(s);
IF ReadBlockCrc(handle, nr, blockPtr) = noError THEN
IF (handle^.group = private) THEN
CatFiles.Seek(blockPtr^.Start+LONG(blockPtr^.hLength)-TSIZE(pInfoType), handle^.datHandle, CatFiles.start);
CatFiles.ReadMuch(TSIZE(pInfoType), handle^.datHandle, ADR(pers));
own := pers.locked # 0C;
state := pers.Status;
ELSE
own := FALSE;
state := '?';
END;
bits := blockPtr^.bits;
IF (bOldComToOwnMessage IN bits) THEN
INCL(bits, bComToOwnMessage);
EXCL(bits, bOldComToOwnMessage);
blockPtr^.bits := bits;
WriteBlockCrc(handle, nr, blockPtr);
END;
ELSE
own := FALSE;
state := '?';
bits := {};
END;
END ReadPersState;
PROCEDURE ReadRightNumber(handle : OneGroupHandle; nr : CARDINAL; VAR right : CARDINAL);
(* Nummer der Nachricht in der Kommentarverkettung nach rechts erfragen *)
VAR s : pBlock; blockPtr : pBlockPtr;
BEGIN
blockPtr := ADR(s);
IF ReadBlockCrc(handle, nr, blockPtr) = noError THEN
right := blockPtr^.rightMess;
ELSE
right := empty;
END;
END ReadRightNumber;
PROCEDURE HasAnswer(handle : OneGroupHandle; nr : CARDINAL; VAR bits : BITSET):BOOLEAN;
(* Testet, ob diese Msg noch eine Antwort hat, fr grin.grinResetState *)
VAR s : pBlock; blockPtr : pBlockPtr;
BEGIN
blockPtr := ADR(s);
bits := {};
IF ReadBlockCrc(handle, nr, blockPtr) = noError THEN
bits := blockPtr^.bits;
RETURN blockPtr^.downMess # empty;
ELSE
RETURN FALSE
END;
END HasAnswer;
PROCEDURE ReadText(handle : OneGroupHandle; nr : CARDINAL; adr : ADDRESS);
(* den Text zur msg in den Puffer lesen, mu natrlich gro genug sein! *)
VAR s : pBlock; blockPtr : pBlockPtr;
BEGIN
blockPtr := ADR(s);
IF ReadBlockCrc(handle, nr, blockPtr) = noError THEN
WITH blockPtr^ DO
ReadFromDat(handle, Start+LONG(hLength), LONG(Length), adr);
END;
END;
END ReadText;
PROCEDURE iReadMessage(handle : OneGroupHandle;
nr : CARDINAL;
text : BOOLEAN; (* Text auch einlesen? *)
VAR mess : MessageType);
(* Eine Msg aus der Datenbank lesen *)
VAR s : pBlock; blockPtr : pBlockPtr;
BEGIN
blockPtr := ADR(s);
error := ReadBlock(handle, nr, blockPtr);
IF error = noError THEN
iReadHeader(handle, nr, blockPtr, mess);
IF error = noError THEN
iReadRest(handle, blockPtr, text, mess);
END;
END;
END iReadMessage;
(* Mgliche Fehler: noError, notFound, IOError *)
PROCEDURE ReadMessage(handle : OneGroupHandle;
nr : CARDINAL;
VAR mess : MessageType); (* exported *)
(* Eine Msg aus der Datenbank lesen *)
BEGIN
iReadMessage(handle, nr, TRUE, mess);
END ReadMessage;
PROCEDURE ReadHeader(handle : OneGroupHandle; nr : CARDINAL; VAR mess : MessageType);
(* exported *)
(* Nur einen Teil der Msg lesen, evtl. weitere Funktionen *)
BEGIN
iReadMessage(handle, nr, FALSE, mess);
END ReadHeader;
PROCEDURE ReadSmallHeader(handle : OneGroupHandle; nr : CARDINAL; VAR mess : MessageType);
(* Nur einen Teil der Msg lesen, evtl. weitere Funktionen *)
VAR s : pBlock; blockPtr : pBlockPtr;
BEGIN
blockPtr := ADR(s);
error := ReadBlock(handle, nr, blockPtr);
IF error = noError THEN
iReadHeader(handle, nr, blockPtr, mess);
END;
END ReadSmallHeader;
(*--- Vernderungen whrend des normalen Lesens ---*)
PROCEDURE SomethingRead(handle : OneGroupHandle; nr : CARDINAL; newread : BOOLEAN);
(* group : Gruppe der Msg *)
(* nr : Nummer der Msg *)
(* newread : Wurde sie neu gelesen oder ungelesen? *)
(* Wird von SetBits automatisch aufgerufen *)
VAR start : CARDINAL;
posi : CARDINAL;
BEGIN
IF newread THEN
IF GetOnePos(handle^.group, unreadCount) > 0 THEN (* alles klar.. *)
SetOnePos (handle^.group, unreadCount, GetOnePos (handle^.group, unreadCount) - 1);
start := nr;
IF GetOnePos (handle^.group, unreadPos) < start THEN
start := GetOnePos (handle^.group, unreadPos)
END;
(* Nchste ungelesene Msg suchen: *)
posi := GetOnePos (handle^.group, unreadPos);
SearchNCountNew(handle, start, posi, v.card, TRUE);
SetOnePos (handle^.group, unreadPos, posi)
ELSE (* irgendwas stimmt nicht, mal neu zhlen & suchen *)
forceUnreadRefresh(handle);
END;
END;
END SomethingRead;
PROCEDURE SetBits(handle : OneGroupHandle;
nr : CARDINAL;
bits : BITSET); (* exported *)
(* Statusbits setzen *)
VAR s : pBlock; blockPtr : pBlockPtr; newread : BOOLEAN; unread : BOOLEAN;
BEGIN
blockPtr := ADR(s);
error := ReadBlock(handle, nr, blockPtr);
newread := ~(bGelesen IN blockPtr^.bits) & (bGelesen IN bits);
unread := (bGelesen IN blockPtr^.bits) & ~(bGelesen IN bits);
blockPtr^.bits := bits;
IF (bOldComToOwnMessage IN bits) THEN
INCL(bits, bComToOwnMessage);
EXCL(bits, bOldComToOwnMessage);
blockPtr^.bits := bits;
WriteBlockCrc(handle, nr, blockPtr);
END;
WriteBlockCrc(handle, nr, blockPtr); (* erst ndern, ist wichig fr die Suche gleich *)
IF newread THEN SomethingRead(handle, nr, TRUE); END;
IF unread THEN forceUnreadRefresh (handle) END;
END SetBits;
PROCEDURE SetState(handle : OneGroupHandle;
nr : CARDINAL;
date : LONGCARD;
State : CHAR);
(* StatusChar setzen *)
VAR s : pBlock; blockPtr : pBlockPtr; p : pInfoType;
BEGIN
blockPtr := ADR(s);
error := ReadBlock(handle, nr, blockPtr);
IF mPrivateBytes IN blockPtr^.items THEN
WITH blockPtr^ DO
ReadFromDat(handle, Start+LONG(hLength)-TSIZE(pInfoType), TSIZE(pInfoType), ADR(p));
IF (p.Status # State) OR (p.LeseDatum # date) THEN
p.Status := State;
p.LeseDatum := date;
WriteToDat(handle, Start+LONG(hLength)-TSIZE(pInfoType), TSIZE(pInfoType), ADR(p));
END;
END; (* WITH blockPtr^ DO *)
END;
END SetState;
PROCEDURE ChangeState(handle : OneGroupHandle;
nr : CARDINAL;
NewState : CHAR); (* exported *)
(* StatusChar setzen *)
VAR l : LONGCARD;
BEGIN
(*
GetActualDate(l);
*)
SetState(handle, nr, ConvertDate.CurrentDate(), NewState);
(* Was bei Statuswechsel von Z -> G oder Z -> B oder x -> Z ? *)
END ChangeState;
(*
TYPE SearchType = (inBits, inSubject, inText, changeBits, dontShow, reverse);
(* Die ersten drei geben je an, wo gesucht werden soll. changeBits sagt, *)
(* da die Bits der gefundenen Msg gem der Setzmaske gesetzt werden *)
(* sollen. Bei dontShow werden alle Msgs bis zum Ende behandlet. Mit *)
(* reverse kann man die Suchrichtung auf rckwrtes stellen *)
TYPE SearchSet = SET OF SearchType;
*)
PROCEDURE ComplexSearch(handle : OneGroupHandle;
start : CARDINAL; (* StartNachricht *)
setBits,
clearBits : BITSET; (* Zusammen Suchmaske *)
VAR str, str2,
str3, str4 : ARRAY OF CHAR; (* Zu suchender String *)
wo1, wo2,
wo3, wo4 : INTEGER; (* Wo sollen sie stehen? *)
verkn1, verkn2,
verkn3 : INTEGER; (* logische Verknpfung *)
gross : BOOLEAN; (* gro=klein? *)
toSetBits,
toClearBits : BITSET; (* Zusammen Setzmaske *)
what : SearchSet; (* Was machen? *)
VAR break : BOOLEAN; (* Abbruch durch esc? *)
VAR nr : CARDINAL):BOOLEAN; (* Nummer, falls gefunden *)
(* Sucht eine Nachricht nach den angegebenen Werten, falls sie gefunden wird *)
(* dann ist found TRUE und es wird die Nummer dieser Nachricht zurckgegeben *)
(* str wird evtl. wegen gro-klein-Unterscheidung gendert, deswegen *)
(* wo1..wo4, verkn1..verkn3 wie in SearchHelp.. *)
TYPE cardPtrType = POINTER TO ARRAY[0..MAX(CARDINAL)] OF CARDINAL;
VAR found : BOOLEAN;
s : pBlock;
blockPtr : pBlockPtr;
error : errorType;
(* Tabelle : BoyerMoore.tableType; *)
pPatt, pPatt2,
pPatt3, pPatt4 : Find2.tpPattern;
(* Tabelle der Zeichenpositionen im String (von hinten) *)
PROCEDURE isTheEnd(start : CARDINAL):BOOLEAN;
(* Hier auch die Anpassung fr Rckwrtssuche *)
BEGIN
RETURN ((start >= handle^.anz) & ~(reverse IN what)) OR ((start = $FFFF) & (reverse IN what));
END isTheEnd;
PROCEDURE nextMess(VAR start : CARDINAL);
BEGIN
IF reverse IN what THEN DEC(start) ELSE INC(start); END;
END nextMess;
PROCEDURE BitsOk(set, clear, messBits : BITSET;
VAR result : BOOLEAN):BOOLEAN;
VAR mask : BITSET;
BEGIN
mask := set+clear;
messBits := messBits - (messBits - mask);
result := messBits = set;
RETURN result;
END BitsOk;
PROCEDURE SetBits(set, clear : BITSET; VAR messBits : BITSET);
BEGIN
IF (changeBits IN what) & ~(ask IN what) THEN
messBits := messBits + set;
messBits := messBits - clear;
END;
END SetBits;
PROCEDURE stopSearch():BOOLEAN;
VAR char, scan : CHAR; kstate : BITSET;
BEGIN
IF isKey (kstate, scan, char)
THEN
RETURN char = 33C;
END;
RETURN FALSE;
(*
RETURN isKey(kstate, scan, char) & (char = 33C);
*)
END stopSearch;
PROCEDURE CheckWhere(cardPtr : cardPtrType;
idLength : CARDINAL;
hLength : CARDINAL;
bits : BITSET;
cond : INTEGER;
start : CARDINAL;
len : CARDINAL):BOOLEAN;
(* Testet, ob der String an der gewnschten Stelle steht *)
(* Wegen der &%$%$%$%-68000er muss hier eine Sonderbehandlung her, *)
(* um ungerade Adressen zu umgehen *)
TYPE TrickType = RECORD
CASE :CARDINAL OF
0 : card : CARDINAL|
1 : byte : ARRAY[0..1] OF BYTE|
END;
END;
TrickArray = ARRAY[0..MAX(CARDINAL)] OF BYTE;
VAR c : CARDINAL;
TrickPointer : POINTER TO TrickArray;
t : ARRAY[0..4] OF TrickType;
(* Keine Lngenberprfung, da ein 0C am Ende jedes Stringes steht *)
(* Also kann kein gefundener String ber ein Feldende hinausgehen! *)
BEGIN
TrickPointer := ADDRESS(cardPtr);
FOR c := 0 TO 4 DO
t[c].byte[0] := TrickPointer^[2*c];
t[c].byte[1] := TrickPointer^[2*c+1];
END;
(* evtl. wird hier auf nicht vorhandene Cardinals zugegriffen, das ist aber *)
(* nicht weiter tragisch, da die Werte in diesen Fllen auch nicht beachtet *)
(* werden! *)
CASE cond OF
SearchHelp.sUEBERALL : RETURN (start >= 0) & (start < idLength) OR (start >= idLength + ((*cardPtr^[0]*)t[0].card+1)*2)
(* Also in der ID gefunden oder aber hinter der CARDINAL-Tabelle *)
| SearchHelp.sTEXT : RETURN start >= hLength
| SearchHelp.sBETREFF : RETURN (start >= (*cardPtr^[1]*)t[1].card) &
( ((*cardPtr^[0]*)t[0].card >= 2) & (start < (*cardPtr^[2]*)t[2].card) OR
((*cardPtr^[0]*)t[0].card < 2) & (start < hLength)
)
| SearchHelp.sTEXTnBETREFF : RETURN (start >= (*cardPtr^[1]*)t[1].card) &
( ((*cardPtr^[0]*)t[0].card >= 2) & (start < (*cardPtr^[2]*)t[2].card) OR
((*cardPtr^[0]*)t[0].card < 2) & (start < hLength)
)
OR (start >= hLength)
| SearchHelp.sABSENDER : RETURN
(mVon IN bits) & (start >= (*cardPtr^[2]*)t[2].card) &
( ((*cardPtr^[0]*)t[0].card >= 3) & (start < (*cardPtr^[3]*)t[3].card) OR
((*cardPtr^[0]*)t[0].card < 3) & (start < hLength)
)
| SearchHelp.sEMPFAENGER : IF mVon IN bits THEN c := 3; ELSE c := 2; END;
(* Position der Startangabe berechnen *)
RETURN
(mAn IN bits) & (start >= (*cardPtr^[c]*)t[c].card) &
( ((*cardPtr^[0]*)t[0].card >= c+1) & (start < (*cardPtr^[c+1]*)t[c+1].card) OR
((*cardPtr^[0]*)t[0].card < c+1) & (start < hLength)
)
| SearchHelp.sID : RETURN (start >= 0) & (start < idLength) & (len <= idLength) |
END;
END CheckWhere;
PROCEDURE MakeNewCardPtr (VAR ptr: ADDRESS);
VAR count: CARDINAL;
bPtr: POINTER TO BYTE;
newPtr: ADDRESS;
BEGIN
bPtr := ptr;
count := ORD(bPtr^)*256; INC (bPtr); INC (count, ORD(bPtr^));
ALLOCATE (newPtr, (count+1) * TSIZE (CARDINAL));
Block.Copy (ptr, (count+1) * TSIZE (CARDINAL), newPtr);
ptr := newPtr;
END MakeNewCardPtr;
PROCEDURE FindWithBits(VAR break : BOOLEAN):BOOLEAN;
VAR found : BOOLEAN;
helpPtr : ADDRESS; (* BoyerMoore.longPtr; *)
cardPtr : ADDRESS;
calcLen,
datStart : LONGCARD;
PROCEDURE FindWithWhere (Text: ADDRESS; (* Anfangsadresse des zu durchsuchenden Textes *)
TextLen: LONGINT; (* Gesamtlnge des Textes *)
p : Find2.tpPattern; (* Zeiger auf vorcompiliertes Suchmuster *)
Start : LONGINT; (* Such-Startposition im Text *)
wo : INTEGER
): BOOLEAN;
(* Sucht einen Text mit berprfung der Fundstelle auf Korrektheit *)
(* Achtung: Die Daten zur berprfung gehen nicht ber die Prozedurschnittstelle! *)
VAR found, textfound : BOOLEAN; save : ADDRESS;
BEGIN
REPEAT
textfound := Find2.Find (Text, TextLen, p, 0);
found := textfound & CheckWhere(cardPtr, blockPtr^.idLength, blockPtr^.hLength, blockPtr^.items, wo,
SHORT(LONGCARD(p^.pFirst^.pMatch-helpPtr)), LENGTH(str));
save := Text;
Text := p^.pFirst^.pMatch+ADDRESS(1);
TextLen := LONGINT(Text)-LONGINT(save)+1;
UNTIL ~textfound OR found;
RETURN found;
END FindWithWhere;
BEGIN
REPEAT
(* Ist eine zweite REPEAT-Schleife vielleicht besser? *)
found := FALSE; (* gonna be pessimistic today *)
blockPtr := ADR(s);
error := ReadBlock(*Crc*)(handle, start, blockPtr);
(* Naja, auch mal anpassen, falls wirklich ein bler Fehler auftritt *)
(* Erstmal nach einer Message mit den passenden Bits suchen *)
WHILE (error = noError) & (* Suchen solange kein Dateifehler.. *)
~isTheEnd(start) & (* .. und noch kein Ende erreicht.. *)
~BitsOk(setBits,
clearBits,
blockPtr^.bits, (* .. sowie die Bits nicht stimmen. *)
found) DO (* Dabei das Ergebnis merken. *)
(* erstmal in den Bits suchen, da geht viel schneller *)
nextMess(start);
IF ~found & ~isTheEnd(start) THEN
blockPtr := ADR(s);
error := ReadBlock(*Crc*)(handle, start, blockPtr);
END;
END; (* WHILE *)
IF found & (*((inSubject IN what) OR *)(inText IN what) THEN
(* -- Nachricht laden und durchsuchen *)
WITH blockPtr^ DO
(* -- Lnge des bentigten Blocks berechnen *)
calcLen := LONG(Length)+LONG(hLength);
datStart := Start;
(* -- Anfordern.. *)
Storage.ALLOCATE(helpPtr, calcLen);
IF helpPtr # NIL THEN
CatGlobal.busyMouse();
(*-- laden.. *)
ReadFromDat(handle, datStart, calcLen, helpPtr);
(* -- und durchsuchen *)
(* found := Find2.Find (helpPtr, LONGINT(calcLen), pPatt, 0); *)
cardPtr := helpPtr + ADDRESS(LONG(blockPtr^.idLength));
IF ODD(blockPtr^.idLength) THEN cardPtr := cardPtr + 1 END;
MakeNewCardPtr (cardPtr);
(* Zeiger auf die Anfangsoffsets der einzelnen Strings in der Datenbank *)
found := FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt, 0, wo1);
(* Jetzt noch logische Verknpfungen machen *)
IF pPatt2 # NIL THEN
(* der erste leere String bricht ab *)
IF verkn1 = SearchHelp.vUND THEN
found := found & FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt2, 0, wo2)
ELSE
found := found OR FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt2, 0, wo2)
END;
IF pPatt3 # NIL THEN
(* der erste leere String bricht ab *)
IF verkn2 = SearchHelp.vUND THEN
found := found & FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt3, 0, wo3)
ELSE
found := found OR FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt3, 0, wo3)
END;
IF pPatt4 # NIL THEN
(* der erste leere String bricht ab *)
IF verkn3 = SearchHelp.vUND THEN
found := found & FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt4, 0, wo4)
ELSE
found := found OR FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt4, 0, wo4)
END;
END; (* pPatt4 *)
END; (* pPatt3 *)
END; (* pPatt2 *)
(*
found := BoyerMoore.Pos(0,
helpPtr,
LONGINT(calcLen),
str,
LENGTH(str),
gross,
Tabelle) <= LONGINT(calcLen);
*)
Storage.DEALLOCATE(helpPtr, 0);
Storage.DEALLOCATE (cardPtr, 0);
ELSE
MTE.noMemAlert();
RETURN FALSE;
END;
END; (* WITH blockPtr^ *)
END;
IF found THEN
(* Hier werden jetzt die Flags gesetzt! *)
SetBits(toSetBits, toClearBits, blockPtr^.bits);
WriteBlockCrc(handle, start, blockPtr);
IF dontShow IN what THEN
found := FALSE; (* hihi *)
nextMess(start); (* aktuelle Msg berblttern *)
END;
ELSE (* Hier kommen wir an, wenn die Bits richtig sind, aber der Text nicht! *)
nextMess(start);
(* Oben erfolgt auch eine Erhhung, sogar die wichtigere! *)
(* Hier unten wird die Nachricht berblttert, die zwar richtige *)
(* Bits hat, bei der aber der Text falsch ist! *)
END;
break := stopSearch();
UNTIL break OR found OR (error # noError) OR isTheEnd(start);
RETURN found & ~break;
END FindWithBits;
PROCEDURE FindStringOnly(VAR break : BOOLEAN):BOOLEAN;
CONST buffSize = 64L*1024L;
VAR found : BOOLEAN;
helpPtr : ADDRESS; (* BoyerMoore.longPtr; *)
cardPtr : ADDRESS;
freePtr : ADDRESS; (* BoyerMoore.longPtr; *)
amount : LONGCARD; (* Wieviel wurde gelesen *)
startPos : LONGCARD; (* Startposition in der DAT *)
dLen : LONGCARD; (* Dateilnge der DAT *)
where : LONGCARD;
sLeft,
sRight : CARDINAL;
notAgain : BOOLEAN;
(* break : BOOLEAN; *)
PROCEDURE FindWithWhere (Text: ADDRESS; (* Anfangsadresse des zu durchsuchenden Textes *)
TextLen : LONGINT; (* Gesamtlnge des Textes *)
p : Find2.tpPattern; (* Zeiger auf vorcompiliertes Suchmuster *)
Start : LONGINT; (* Such-Startposition im Text *)
wo : INTEGER
): BOOLEAN;
(* Sucht einen Text mit berprfung der Fundstelle auf Korrektheit *)
(* Achtung: Die Daten zur berprfung gehen nicht ber die Prozedurschnittstelle! *)
VAR found, textfound : BOOLEAN; save : ADDRESS;
BEGIN
REPEAT
textfound := Find2.Find (Text, TextLen, p, 0);
found := textfound & CheckWhere(cardPtr, blockPtr^.idLength, blockPtr^.hLength, blockPtr^.items, wo,
SHORT(LONGCARD(p^.pFirst^.pMatch-helpPtr)), LENGTH(str));
save := Text;
Text := p^.pFirst^.pMatch+ADDRESS(1);
TextLen := LONGINT(Text)-LONGINT(save)+1;
UNTIL ~textfound OR found;
RETURN found;
END FindWithWhere;
BEGIN
Storage.ALLOCATE(freePtr, buffSize);
IF freePtr # NIL THEN
found := FALSE;
dLen := FileLength(handle^.datHandle);
blockPtr := ADR(s);
error := ReadBlock(*Crc*)(handle, start, blockPtr);
REPEAT (* Suchen bis Abbruch oder gefunden oder am Ende *)
(* einlesen *)
IF error # noError THEN (* Wenn wir's nicht lesen knnen, keine Chance.. *)
Storage.DEALLOCATE(freePtr, 0);
RETURN FALSE
END;
WITH blockPtr^ DO
IF reverse IN what THEN
IF Start+LONG(hLength)+LONG(Length) > buffSize THEN
startPos := Start+LONG(hLength)+LONG(Length) - buffSize;
ELSE
startPos := 0;
END;
ELSE
startPos := Start;
END;
amount := BinOps.LowerLCard(buffSize, dLen-startPos);
ReadFromDat(handle, startPos, amount, freePtr);
END; (* WITH blockPtr^ DO *)
(* Wir haben hier eine experimentelle Suchfunktion, die noch ein paar * )
(* Probleme hat. z.B. mu beim rckwrts-Suchen BackPos verwendet werden *)
(* und es gibt Probleme, wenn das Wort gerade auf Puffergrenzen liegt. *)
(* Das kann man dadurch abfangen, da man bestimmt, zu welcher Msg *)
(* das/der Pufferende/-anfang gehrt und entsprechend dies immer komplett *)
(* einliest. Mache ich einmal bei Gelegenheit, viel bringt die Routine *)
(* bei meine Maxtor sowieso nicht. Aber fr langsame Platten und ohne *)
(* Pufferung wre sie doch bei seltenen Worten deutlich schneller. *)
(* Dann kann man auch die Puffergre variabel machen, vielleicht anhand *)
(* anhand einer Bewertungsfunktion, die aus der Lnge und den Buchstaben *)
(* berechnet, wie wahrscheinlich es in einer Msg vorkommen kann. Je *)
(* unwahrscheinlicher, desto grer sollte man den Puffer whlen. *)
IF handle^.parBuff # NIL THEN
(* Wenn die par gepuffert ist, dann einfach den Block durchsuchen *)
notAgain := FALSE;
REPEAT
found := Find2.Find (freePtr, LONGINT(amount), pPatt, 0);
(*
where := BoyerMoore.Pos(0,
freePtr,
LONGINT(amount),
str,
LENGTH(str),
gross,
Tabelle);
found := where <= amount;
*)
IF ~found THEN
IF ~((startPos = 0) & (reverse IN what)) &
~(~(reverse IN what) & (startPos + buffSize > dLen)) THEN
IF reverse IN what THEN
IF startPos > buffSize THEN
DEC(startPos, buffSize);
ELSE
startPos := 0;
END
ELSE
IF startPos + buffSize < dLen THEN
INC(startPos, buffSize);
ELSE
startPos := startPos+buffSize-dLen
END;
END;
amount := BinOps.LowerLCard(buffSize, dLen-startPos);
CatGlobal.busyMouse();
ReadFromDat(handle, startPos, amount, freePtr);
ELSE
notAgain := TRUE;
END;
END;
UNTIL found OR notAgain;
(* Ok, im aktuellen Block steht jetzt mglicherweise das Wort: *)
IF found THEN
(* Jetzt binr die Msg finden, fr die Start <= startPos+where<Start+hLength+Length *)
IF blockPtr^.Start+LONG(blockPtr^.hLength)+LONG(blockPtr^.Length) < startPos + where THEN
(* Gesuchte Stelle ist weiter vorne! *)
sLeft := start; sRight := handle^.anz-1; (* In leeren Gruppen kann man sowieso nicht suchen *)
ELSIF blockPtr^.Start > startPos + where THEN
(* Gesuchte Stelle ist weiter hinten! *)
sLeft := 0; sRight := start;
END;
REPEAT
IF blockPtr^.Start+LONG(blockPtr^.hLength)+LONG(blockPtr^.Length) < startPos + where THEN
(* Gesuchte Stelle ist weiter vorne! *)
sLeft := start;
start := (sLeft+sRight) DIV 2;
IF start = sLeft THEN INC(start) END;
ELSIF blockPtr^.Start > startPos + where THEN
(* Gesuchte Stelle ist weiter hinten! *)
sRight := start;
start := (sLeft+sRight) DIV 2;
IF start = sRight THEN DEC(start) END;
END;
blockPtr := ADR(s);
error := ReadBlock(*Crc*)(handle, start, blockPtr);
UNTIL (blockPtr^.Start <= startPos+where) &
(startPos+where <= blockPtr^.Start+LONG(blockPtr^.hLength)+LONG(blockPtr^.Length));
(* Anschlieend mu in "start" die Nummer dieser Msg stehen! *)
END;
Storage.DEALLOCATE(freePtr, 0);
RETURN found;
ELSE
( * Ende der experimentellen Suchfunktion *)
(* "Alte" Suchfunktion, jetzt auch recht schnell & sicher *)
REPEAT (* Msgs im Puffer einzeln durchsuchen *)
IF error # noError THEN
(* Wenn wir's nicht lesen knnen, keine Chance.. *)
(* hier, da am Ende der Schleife ein neuer Block gelesen wird *)
Storage.DEALLOCATE(freePtr, 0);
RETURN FALSE
END;
helpPtr := freePtr+ADDRESS(blockPtr^.Start-startPos);
cardPtr := helpPtr + ADDRESS(LONG(blockPtr^.idLength));
IF ODD(blockPtr^.idLength) THEN cardPtr := cardPtr + 1 END;
(* Jetzt die Bytes mit den Lngen (Offsets) einfach kopieren, so
* da kein Buserror auftritt
*)
MakeNewCardPtr (cardPtr);
(* Zeiger auf die Anfangsoffsets der einzelnen Strings in der Datenbank *)
found := FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt, 0, wo1);
(* Jetzt noch logische Verknpfungen machen *)
IF pPatt2 # NIL THEN
(* der erste leere String bricht ab *)
IF verkn1 = SearchHelp.vUND THEN
found := found & FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt2, 0, wo2)
ELSE
found := found OR FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt2, 0, wo2)
END;
IF pPatt3 # NIL THEN
(* der erste leere String bricht ab *)
IF verkn2 = SearchHelp.vUND THEN
found := found & FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt3, 0, wo3)
ELSE
found := found OR FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt3, 0, wo3)
END;
IF pPatt4 # NIL THEN
(* der erste leere String bricht ab *)
IF verkn3 = SearchHelp.vUND THEN
found := found & FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt4, 0, wo4)
ELSE
found := found OR FindWithWhere (helpPtr, LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)), pPatt4, 0, wo4)
END;
END; (* pPatt4 *)
END; (* pPatt3 *)
END; (* pPatt2 *)
(* Speicher fr CardArray wieder freigeben *)
DEALLOCATE (cardPtr, 0);
(*
found := BoyerMoore.Pos(0,
helpPtr,
LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)),
str,
LENGTH(str),
gross,
Tabelle) <= LONGINT(LONG(blockPtr^.Length)+LONG(blockPtr^.hLength));
*)
IF found THEN
(* Hier werden jetzt die Flags gesetzt! *)
SetBits(toSetBits, toClearBits, blockPtr^.bits);
WriteBlockCrc(handle, start, blockPtr);
IF dontShow IN what THEN
found := FALSE; (* hihi *)
(* nextMess(start); *)
END;
END;
(* Die Sache mit nextMess kann man nicht vereinfachen, da start wichtig ist: *)
(* Returnwert fr die Nummer der gefundenen Nachricht *)
IF ~found THEN
nextMess(start);
IF ~isTheEnd(start) THEN
blockPtr := ADR(s);
IF start MOD 100 = 0 THEN CatGlobal.busyMouse(); END;
error := ReadBlock(*Crc*)(handle, start, blockPtr);
END;
END;
UNTIL found OR isTheEnd(start) OR
(((blockPtr^.Start + LONG(blockPtr^.Length)+LONG(blockPtr^.hLength)) > startPos+amount) & ~(reverse IN what)) OR
((blockPtr^.Start < startPos) & (reverse IN what));
(* Wenn beim vorwrts-Suchen das Ende der Msg nicht mehr drin liegt, oder
beim rckwrts suchen der Anfang nicht mehr ist diese Schleife zuende *)
(* wg. experimenteller Suchfunktion * )
END; (* IF handle^.parBuff # NIL *)
( * bis hier *)
break := stopSearch();
UNTIL break OR found OR isTheEnd(start); (* oben nchsten Puffer einlesen *)
Storage.DEALLOCATE(freePtr, 0);
ELSE
MTE.noMemAlert();
RETURN FALSE;
END;
RETURN found & ~break;
END FindStringOnly;
BEGIN
IF isInSearch THEN RETURN FALSE END;
IF start >= handle^.anz THEN RETURN FALSE END;
(* Damit wollen wir doch garnicht erst anfangen.. tststs *)
isInSearch := TRUE;
IF (inText IN what) (*OR (inSubject IN what)*) THEN
(* BoyerMoore.InitTable(Tabelle, str, LENGTH(str), gross, FALSE); *)
pPatt := Find2.Compile (str, '?', 1C, '*', 12C, gross, FALSE);
IF str2[0] = 0C THEN pPatt2 := NIL ELSE
pPatt2 := Find2.Compile (str2, '?', 1C, '*', 12C, gross, FALSE);
END;
IF str3[0] = 0C THEN pPatt3 := NIL ELSE
pPatt3 := Find2.Compile (str3, '?', 1C, '*', 12C, gross, FALSE);
END;
IF str4[0] = 0C THEN pPatt4 := NIL ELSE
pPatt4 := Find2.Compile (str4, '?', 1C, '*', 12C, gross, FALSE);
END;
ELSE
pPatt := NIL; pPatt2 := NIL; pPatt3 := NIL; pPatt4 := NIL;
END; (* Tabelle fr sptere Suche initialisieren *)
mtAppl.StoreMouse();
CatGlobal.busyMouse();
IF CatGlobal.multiTask THEN
(* Bildschirm freigeben und alle Menus sperren
*)
MagicAES.WindUpdate (MagicAES.ENDUPDATE);
END;
IF inBits IN what THEN
found := FindWithBits(break);
ELSE
found := FindStringOnly(break);
END;
IF CatGlobal.multiTask
THEN
(* MagicAES.WindUpdate wieder setzen und Menu wieder anschalten *)
MagicAES.WindUpdate (MagicAES.BEGUPDATE);
END;
mtAppl.RestoreMouse();
nr := start;
Find2.Dispose (pPatt); Find2.Dispose (pPatt2);
Find2.Dispose (pPatt3); Find2.Dispose (pPatt4);
isInSearch := FALSE;
RETURN found;
END ComplexSearch;
(*--- Schreibprozeduren ---*)
PROCEDURE Abort(which : CARDINAL; crcError : BOOLEAN; nr, group : CARDINAL);
(* Abbruchprozedur fr AppendMessage & InserKom *)
VAR scrap : CatTypes.String255;
BEGIN
CatLog.WriteLine('- data.i meldet sich wg. Dateifehler -');
CatLog.WriteString('Dateifehler beim Einfgen in die Gruppe ');
GroupSelect.GroupName(group, scrap);
CatLog.WriteString(scrap);
CatLog.Write('.');
CatLog.WriteLn();
CatLog.WriteString(' -> Gemdos-Fehler #');
CatLog.WriteInt(CatFiles.FileError);
CatLog.WriteLn();
CatLog.WriteString('Und zwar in der ');
CASE which OF
0 : CatLog.WriteLine('Crc-Tabelle.');
| 1 : CatLog.WriteString('Parameterdatei bei MsgNummer ');
CatLog.WriteCard(LONG(nr)); CatLog.Write('.'); CatLog.WriteLn();
| 2 : CatLog.WriteLine('Daten-Hauptdatei.');
END;
CatFiles.GetErrorMsg (CatFiles.FileError, scrap);
CatLog.WriteString ('Fehlermeldung: ');
CatLog.WriteString (scrap);
CatLog.WriteLn();
IF crcError THEN
CatLog.WriteString('Grund: Die Crc der Msg ist falsch.');
CatLog.WriteLn();
END;
CatLog.WriteLine('Daher wurde das Einfgen abgebrochen.');
CatLog.WriteLine('- Ende der Durchsage -');
CatFiles.ErrorAlert(CatFiles.FileError);
END Abort;
PROCEDURE InsertKom(handle : OneGroupHandle;
MessageNummer : CARDINAL;
usenetOnly : BOOLEAN;
VAR addFlags : BITSET; (* Flags, die wg. Vererben-Flag gesetzt werden sollen *)
VAR upMess, leftMess : CARDINAL;
VAR refNr : ARRAY OF CHAR; (* eigentlich reference-parameter *)
VAR abort : BOOLEAN);
VAR origin, Nr,
last, next : CARDINAL;
s : pBlock;
blockPtr : pBlockPtr;
err : errorType;
eindeutig : BOOLEAN;
searchAgain : BOOLEAN;
scrapID : String1023; (* Testen, ob die ID ok ist *)
filteredToRead: BOOLEAN;
PROCEDURE SearchWithHash(VAR origin : CARDINAL):BOOLEAN;
VAR compareCrc : CARDINAL;
BEGIN
IF Hashing2.emptyHash(handle^.hash) THEN RETURN FALSE END;
(* Keine Hashtabelle, SearchAgain sowieso TRUE, d.h. nochmal suchen *)
compareCrc := CalcIdCrc(refNr);
origin := Hashing2.GetFirst(handle^.hash, compareCrc);
(* Erste Msg mit gesuchter Crc oder empty *)
eindeutig := Hashing2.GetNext(handle^.hash) = empty;
(* Falls es keinen weiteren gibt, ist es eindeutig *)
searchAgain := ~eindeutig;
(* nicht eindeutig => nochmal suchen *)
(* Sonst haben wir einen Kandidaten, dann nicht nochmal suchen *)
RETURN eindeutig & (origin # empty); (* gefunden und eindeutig *)
END SearchWithHash;
PROCEDURE SearchUsenet(handle : OneGroupHandle;
VAR search : ARRAY OF CHAR;
VAR origin : CARDINAL;
VAR start : CARDINAL):BOOLEAN;
(* Sucht eine Usenet-ID *)
VAR id : CatTypes.String255;
nr : CARDINAL;
isOldDupe : BOOLEAN;
BEGIN
nr := start;
WHILE nr < CARDINAL(-1) DO
IF ReadOtherRId(handle, nr, id, isOldDupe) THEN
(* IF MagicStrings.Equal(id, search) THEN *)
IF AssFuncs.CmpId(id, search) THEN
origin := nr;
RETURN TRUE;
END;
END;
DEC(nr);
END;
RETURN FALSE
END SearchUsenet;
BEGIN
addFlags := {}; (* erstmal nix *)
(* Erstmal initialisieren, dann kann man ohne Probleme RETURNen *)
upMess := notSaved;
leftMess := empty;
eindeutig := FALSE;
(* abort := FALSE; *)
(* Ist die crc eindeutig? Dann beim Herstellen der Verkettung die ID untersuchen *)
searchAgain := TRUE;
IF (usenetOnly & SearchUsenet(handle, refNr, origin, MessageNummer)) OR
(~usenetOnly &
(
SearchWithHash(origin) OR
(searchAgain & SearchID(handle, refNr, 0, TRUE, TRUE, origin))
)
)
THEN
(* Zur neuesten verketten ^^^^ *)
(* Erluterung: Der erste Term ist falsch, wenn entweder keine Hashtabelle da ist *)
(* oder aber dort die gesuchte Crc nicht gefunden wurde oder sie nicht eindeutig *)
(* ist. Falls sie nicht eindeutig ist, oder keine Hashtabelle da ist, soll nach *)
(* alten Methode gesucht werden. *)
blockPtr := ADR(s);
err := ReadBlockCrc(handle, origin, blockPtr); (* Hier Fehlerbehandlung!! *)
IF err # noError THEN Abort(1, err = crcError, origin, handle^.group); abort := TRUE; RETURN END;
IF bVererben IN blockPtr^.bits THEN
(* hier wird berprft, ob die Muttermsg das Vererben-Flag hat und wenn ja, *)
(* dann werden alle Flags an die Tochtermsg weitergegeben *)
addFlags := blockPtr^.bits - {bGelesen, bKommentieren, bAntworten, bOwnMessage, bComToOwnMessage};
IF (bFiltered IN addFlags)
THEN
ConfVars.GetConfDefBool (cFilteredRead, filteredToRead, FALSE);
IF filteredToRead
THEN
INCL (addFlags, bGelesen);
END;
END;
END;
IF bOwnMessage IN blockPtr^.bits THEN
(* Kommentare auf eigene Nachrichten erhalten ein spezielles Flag *)
INCL (addFlags, bComToOwnMessage);
(* Jetzt noch merken, da es in dieser Gruppe Kommentare auf
* eigene Nachrichten gab
*)
END;
IF eindeutig THEN
(* Falls die Crc eindeutig war, mu noch die ID selber berprft werden *)
ReadID(handle, blockPtr^.Start, blockPtr^.idLength, scrapID);
IF error # noError THEN Abort(2, error = crcError, origin, handle^.group); abort := TRUE; RETURN END;
(* IF ~MagicStrings.Equal(scrapID, refNr) THEN *)
IF ~AssFuncs.CmpId (scrapID, refNr) THEN
RETURN
END;
END;
upMess := origin;
Nr := origin; (* Fr's Schreiben unten *)
IF (blockPtr^.downMess = empty) OR (blockPtr^.downMess <= origin) THEN
(* Im zweiten Fall wr's ein Verkettungsfehler -> anmeckern *)
blockPtr^.downMess := MessageNummer;
blockPtr^.KomCount := 1;
leftMess := empty;
ELSE (* blockPtr^.downMess # empty *)
(* Jetzt beim ersten Kommentar, nach rechts bis zum Ende gehen *)
last := origin; (* Ursprungmsg, bzw. kommentierte Msg *)
next := blockPtr^.downMess;
INC(blockPtr^.KomCount); (* Jetzt haben wir einen Kommentar mehr *)
(* blockPtr^.crc := CalcCrcArray(blockPtr+ADDRESS(2), SHORT(TSIZE(pBlock))-2); *)
WriteBlockCrc(handle, origin, blockPtr);
IF error # noError THEN Abort(1, err = crcError, origin, handle^.group); abort := TRUE; RETURN END;
(* ..und jetzt suchen, bis wir am Ende des Kommentarbaumes sind *)
WHILE (last < next) & (next < handle^.anz) & (next # empty) DO
blockPtr := ADR(s);
err := ReadBlockCrc(handle, next, blockPtr); (* Fehlerbehandlung..! *)
IF err # noError THEN Abort(1, err = crcError, next, handle^.group); abort := TRUE; RETURN END;
last := next;
next := blockPtr^.rightMess
END;
(* Verkettungsfehler anmeckern *)
Nr := last;
blockPtr^.rightMess := MessageNummer;
leftMess := Nr;
END;
(* blockPtr^.crc := CalcCrcArray(blockPtr+ADDRESS(2), SHORT(TSIZE(pBlock))-2);*)
WriteBlockCrc(handle, Nr, blockPtr);
IF error # noError THEN Abort(1, error = crcError, Nr, handle^.group); abort := TRUE; RETURN END;
END
END InsertKom;
(*
CONST bId = 0; bRefNr = 1; bVon = 3; bAn = 4; bWegen = 5;
bEZeit = 6; bGruppe = 7; bBSZeit = 8; bMId = 9; bRId = 10;
bBox = 11; bName = 12; bText = 13; bTextDatei = 14;
TYPE PtrRecord =
RECORD
whatsThere : BITSET;
pId, (* Maus-ID der Mitteilung *)
pRefNr, pVon, (* kommentierte Msg, Absender *)
pAn, pWegen, (* Empfnger, Betreff *)
pEZeit, pGruppe, (* Eingabezeit, Gruppe *)
pBSZeit, pMId, (* Bearb.status+Zeit, MessageID *)
pRId, pBox, (* RId gem. Def., Box gem. Def. *)
pName : Str1023Ptr; (* Name gem. Def falls # Absenderangabe *)
pText : BigTextPtr; (* MsgText *)
TextMax : CARDINAL;
txt : mtTextfiles.TEXTFILE;
END;
*)
(*TYPE WhatsThat = (own, personal, status, normal, garbage);*)
PROCEDURE PreCheck(VAR mess : PtrRecord):WhatsThat; (* exported *)
(* Testet die Nachricht einmal durch, damit der parser schon einen Teil der *)
(* Fehlerbehandlung sowie die WatchDog-Behandlung machen kann *)
BEGIN
(* Persnliche Nachricht? *)
IF (stringSet{bVon, bAn, bWegen, bEZeit, bBSZeit, bText} - mess.whatsThere = stringSet{}) &
~(bGruppe IN mess.whatsThere) THEN RETURN personal
ELSIF (* Eigene, persnliche Nachricht *)
(stringSet{ bAn, bWegen, bEZeit, bTextDatei} - mess.whatsThere = stringSet{}) &
~(bGruppe IN mess.whatsThere) THEN RETURN own
ELSIF (* Normale Gruppen-Nachricht? *)
(stringSet{ bWegen, bEZeit, bGruppe, bText} - mess.whatsThere = stringSet{})
(*& ~(bBSZeit IN mess.whatsThere) wg. "RAUS" *)THEN RETURN normal
ELSIF stringSet{bId, bBSZeit, bEZeit} - mess.whatsThere = stringSet{} THEN RETURN status
ELSE
RETURN garbage
END;
END PreCheck;
PROCEDURE isDupe(handle : OneGroupHandle; VAR mess: PtrRecord; date: LONGCARD; type: WhatsThat):BOOLEAN;
VAR nr : CARDINAL;
s : pBlock;
blockPtr : pBlockPtr;
oldMess: MessageType;
BEGIN
IF ~SearchID(handle, mess.pId^, 0, TRUE, TRUE, nr) THEN
RETURN FALSE
ELSE
blockPtr := ADR(s);
IF ReadBlockCrc(handle, nr, blockPtr) = noError THEN
IF (blockPtr^.Datum = date)
& (blockPtr^.Length = mess.TextMax)
THEN
RETURN TRUE; (* doch'n Dupe *)
ELSE
IF (type = own) & (blockPtr^.Datum = date)
THEN
(* Da ist die Lnge noch nicht gesetzt, daher Dupe! *)
RETURN TRUE;
ELSIF (type = own)
THEN
(* Mal sehen, ob wir eine MId haben *)
IF (bMId IN mess.whatsThere) & (mMId IN blockPtr^.items)
THEN
(* MId ist da, alte MId lesen *)
ReadHeader (handle, nr, oldMess);
IF (mess.pMId # NIL)
& (oldMess.mid # NIL)
THEN
IF AssFuncs.CmpId (mess.pMId^, oldMess.mid^)
THEN
DEALLOCATE (oldMess.InfoStrings, 0);
RETURN TRUE;
END;
END;
DEALLOCATE (oldMess.InfoStrings, 0);
END;
END;
INCL(blockPtr^.bits, bOldDupe);
WriteBlockCrc(handle, nr, blockPtr);
RETURN FALSE; (* Kein Dupe, aber gleiche ID *)
END;
ELSE
RETURN FALSE (* Gibt's einen Dateifehler ist's auch kein Dupe :-) *)
END;
END;
END isDupe;
PROCEDURE AppendMessage(handle : OneGroupHandle; (* exported *)
type : WhatsThat;
bitWishes : BITSET;
(* Bits, die beim Einfgen schon gesetzt werden sollen; bFiltered bisher *)
VAR mess : PtrRecord;
VAR usenetChain, usenetOk : BOOLEAN;
VAR isOneDupe: BOOLEAN;
VAR abort : BOOLEAN):BOOLEAN;
(* Eine Message bearbeiten/ abort -> sofort abbrechen, schwerer Fehler *)
(* Returnwert zeigt an, ob die Nachricht geschrieben wurde *)
(* Es knnen natrlich nur die Typen own, personal, status und normal geschrieben *)
(* werden. Bei Mibrauch: Garbage in possible crash out! *)
(* Eine Message bearbeiten/ abort -> sofort abbrechen, schwerer Fehler *)
(* Rckgabe, ob sie geschrieben wurde *)
VAR block : pBlock;
z : CARDINAL;
CRC : CARDINAL;
pInfo : pInfoType;
pDupe : dupeInfoPtr; (* Fr redundante Daten in der *.DAT *)
len,
l2 : ARRAY[0..32] OF CARDINAL; (* Darein kommt der Lngen-Header *)
lauf : stringTypes;
str : Str1023Ptr; (* Allzweckpointer.. *)
saveChar : CHAR;
saveCh2 : CHAR;
ptRId : CatTypes.Str1023Ptr;
inserted : BOOLEAN; (* Flag, ob Nachricht mit RId eingefgt wurde,
* ansonsten wird noch Wildwest versucht *)
charPos : CARDINAL; (* beides fr die Kommentarverkettung per RId *)
ptPos : CARDINAL; (* ebenfalls fr RId-Verkettung *)
addBits : BITSET; (* Zustzlich zu setzende Bits, die wg. VererbenFlag von der *)
(* Muttermsg bernommen werden sollen *)
PROCEDURE Transfer(VAR len : CARDINAL; txt : mtTextfiles.TEXTFILE);
(* Text aus Textdatei in die Datenbank packen *)
VAR scrap : CatTypes.String1023;
scrap2: CatTypes.String1023;
l : CARDINAL;
tabSize: INTEGER;
BEGIN
ConfVars.GetConfDefInt (cTabsize, tabSize, 4);
len := 0;
REPEAT
mtTextfiles.ReadLine(txt, scrap);
mtTextfiles.ReadLn(txt);
CatGlobal.ConvertTabs (scrap, scrap2, tabSize);
l := LENGTH(scrap2);
scrap2[l] := LF;
INC(l, 1);
AppendToDat(handle, ADR(scrap2), LONG(l), v.lcard);
INC(len, l);
UNTIL mtTextfiles.EndofText(txt) OR (CatFiles.FileError < 0);
END Transfer;
PROCEDURE Str2Ptr(str : stringTypes):ADDRESS;
BEGIN
CASE str OF
bWegen : RETURN mess.pWegen; |
bVon : RETURN mess.pVon; |
bAn : RETURN mess.pAn; |
bMId : RETURN mess.pMId; |
bRId : RETURN mess.pRId; |
bBox : RETURN mess.pBox; |
bName : RETURN mess.pName; |
bRefNr : RETURN mess.pRefNr; |
bDistribution : RETURN mess.pDistribution; |
bGate : RETURN mess.pGate; |
bMime : RETURN mess.pMime; |
bFollowupTo : RETURN mess.pFollowupTo; |
bReplyTo : RETURN mess.pReplyTo; |
bSender : RETURN mess.pSender; |
ELSE
HALT;
END;
END Str2Ptr;
PROCEDURE item(str : stringTypes):CARDINAL;
BEGIN
CASE str OF
bVon : RETURN mVon; |
bAn : RETURN mAn; |
bMId : RETURN mMId; |
bRId : RETURN mRId; |
bBox : RETURN mBox; |
bName : RETURN mName; |
bRefNr : RETURN mRefNr; |
bGate : RETURN mGate; |
bMime : RETURN mMime; |
bFollowupTo : RETURN mFollowup; |
bReplyTo: RETURN mReplyTo; |
bSender: RETURN mSender; |
bDistribution : RETURN mDistribution; |
ELSE
HALT;
END;
END item;
PROCEDURE isMouse(ptr : ADDRESS):BOOLEAN;
VAR p : CatTypes.Str255Ptr;
BEGIN
p := ptr;
RETURN MagicStrings.Equal(p^, ".maus.de")
END isMouse;
BEGIN
usenetChain := FALSE; usenetOk := FALSE;
isOneDupe := FALSE;
(* default: Keine Usenetverkettung, hat also auch nicht geklappt *)
abort := FALSE; (* Es geht nichts schief.. :-) *)
(* bId sollte immer gesetzt sein *)
IF type = status THEN (** Statusmeldung **)
IF SearchID(handle, mess.pId^, 0, TRUE, TRUE, z) THEN
SetState(handle, z, mDateStr2Long(mess.pBSZeit^, 1), mess.pBSZeit^[0]);
END;
ELSIF (type = personal) OR (type = normal) OR (type = own) THEN
WITH mess DO
WITH block DO
(* Die crc-Prfsumme ber den Block wird weiter unten berechnet *)
IF bEZeit IN whatsThere THEN
Datum := mDateStr2Long(pEZeit^, 0);
ELSE
Datum := ConvertDate.CurrentDate();
(*
GetActualDate(Datum);
*)
END;
(* Das Datum wird fr den Dupecheck jetzt schon bentigt. *)
END;
END;
(* IF isDupe(handle, mess.pId^, block.Datum, type) THEN *)
IF isDupe(handle, mess, block.Datum, type) THEN
IF (type # own)
THEN
CatLog.WriteStringNTime(mess.pId^);
CatLog.WriteLine(' ist ein Dupe und wird nicht eingefgt');
END;
isOneDupe := TRUE;
RETURN FALSE
END;
WITH mess DO
WITH block DO
(* Die crc-Prfsumme ber den Block wird weiter unten berechnet *)
bits := bitWishes;
(* Erst einmal sicherheitshalber zuweisen *)
upMess := empty;
leftMess:= empty;
addBits := {};
IF bRefNr IN whatsThere THEN (* Jetzt evtl. Kommentarverkettung *)
InsertKom(handle, handle^.anz, FALSE, addBits, upMess, leftMess, pRefNr^, abort);
IF abort THEN RETURN FALSE END;
ELSIF bRId IN whatsThere THEN
(* Zuerst die Position vom @ finden *)
inserted := FALSE;
charPos := MagicStrings.Pos('@', pRId^, 0, FALSE);
IF charPos < TSIZE(CatTypes.String255) THEN
charPos := MagicStrings.Pos('.', pRId^, charPos, FALSE);
IF (charPos < TSIZE(CatTypes.String255)) &
(isMouse(ADDRESS(pRId)+ADDRESS(LONG(charPos))))
THEN
saveChar := pRId^[charPos];
pRId^[charPos] := 0C; (* das xxx.maus.de abschneiden *)
(* Neue IDs (kann ich von ausgehen!), jetzt vorne Teil vor . suchen *)
ptPos := MagicStrings.Pos ('.', pRId^, 0, FALSE);
IF ptPos < TSIZE (CatTypes.String255)
THEN
(* . vorne gefunden, wir setzen den Zeiger dahinter! *)
ptRId := ADDRESS (pRId) + ADDRESS(LONG(ptPos+1));
saveCh2 := ptRId^[0];
ptRId^[0] := CAP(ptRId^[0]);
ELSE
ptRId := ADDRESS(pRId);
saveCh2 := 0c;
END;
(* Und jetzt Kommentar einfgen *)
InsertKom(handle, handle^.anz, FALSE, addBits, upMess, leftMess, ptRId^, abort);
inserted := TRUE;
pRId^[charPos] := saveChar; (* ..und wieder herstellen.. *)
IF saveCh2 # 0c
THEN
ptRId^[0] := saveCh2;
END;
ELSIF usenetChaining THEN (* soll die Usenetverkettung hergestellt werden? *)
usenetChain := TRUE;
InsertKom(handle, handle^.anz, TRUE, addBits, upMess, leftMess, pRId^, abort);
(* Verkettung mit Usenet-ID herstellen, langsam.. *)
usenetOk := upMess # notSaved;
inserted := usenetOk;
END;
END;
(* Neu fr Wildwestverkettungen bei Mails mit RId *)
IF ~inserted & (bText IN whatsThere) & PrepareID(ADDRESS(pText), str, saveChar, charPos) THEN
(* Verkettung anhand der ersten Msgzeile, nur fr den Notfall, deswegen *)
(* hier am Ende, mit niedrigster Prioritt. *)
InsertKom(handle, handle^.anz, FALSE, addBits, upMess, leftMess, str^, abort);
str^[charPos] := saveChar; (* wurde in diesem Fall in PrepareID gendert. *)
END;
ELSIF (bText IN whatsThere) & PrepareID(ADDRESS(pText), str, saveChar, charPos) THEN
(* Verkettung anhand der ersten Msgzeile, nur fr den Notfall, deswegen *)
(* hier am Ende, mit niedrigster Prioritt. *)
(* Schneller als Usenet-Verkettung, aber Usenetter haben diese Zeile nicht! *)
InsertKom(handle, handle^.anz, FALSE, addBits, upMess, leftMess, str^, abort);
str^[charPos] := saveChar; (* wurde in diesem Fall in PrepareID gendert. *)
END;
bits := bits + addBits; (* Vererben-Flag *)
downMess := empty;
rightMess := empty;
KomCount := 0;
(* Hier wird der Header in der *.DAT erstellt *)
idLength := LENGTH(pId^)+1;
hLength := idLength;
IF ODD(hLength) THEN INC(hLength) END;
(* Damit man auf die Tabelle auch auf 68000 zugreifen kann *)
(* Zunchst einmal zur Sicherheit, das kann man spter ja *)
(* auch ohne Codenderung wieder weglassen *)
AppendToDat(handle, pId, LONG(hLength), Start);
IF CatFiles.FileError < 0 THEN abort := TRUE; Abort(2, FALSE, 0, handle^.group); RETURN FALSE; END;
items := {};
(* Hier kommt jetzt die Tabelle der Lngen hin *)
l2[1] := hLength;
len[0] := 0; (* Anzahl der Strings, Wegen ist da! *)
FOR lauf := bWegen TO bSender DO
(* bWegen, bVon, bAn, bMId, bRId, bBox, bName, bRefNr, bDistribution, bGate, bMime, bFollowupTo, bReplyTo, bSender *)
IF lauf IN whatsThere THEN
INC(len[0]); (* Wieder einen String gefunden *)
str := Str2Ptr(lauf); (* Pointer abrufen *)
z := LENGTH(str^)+1; (* Lnge inklusive Nullbyte bestimmen *)
INC(hLength, z); (* der Header ist lnger geworden *)
len[len[0]] := z; (* Stringlnge merken *)
IF lauf # bWegen THEN
INCL(items, item(lauf)); (* und auch im Parameterblock merken *)
END;
END;
END;
(* DEBUG: DIE LNGEN SIND KORREKT *)
(* Lnge der Tabelle auf die Startposition addieren: *)
INC(l2[1], (len[0]+1)*2); (* hLength steht schon drin *)
INC(hLength, (len[0]+1)*2);
l2[0] := len[0];
FOR z := 2 TO len[0] DO
l2[z] := len[z-1] + l2[z-1]; (* aus Lngen Startpositionen machen *)
END;
AppendToDat(handle, ADR(l2), LONG(len[0]+1)*2, v.lcard);
(* Tabelle mit den StringLngen *)
IF CatFiles.FileError < 0 THEN abort := TRUE; Abort(2, FALSE, 0, handle^.group); RETURN FALSE; END;
(* Evtl. noch ber einen Puffer laufen lassen? *)
(* Da aber evtl. die Ausgabe schon gepuffer ist *)
(* macht das auch nicht mehr viel aus *)
z := 1; (* Position in len *)
FOR lauf := bWegen TO bSender DO
(* bWegen, bVon, bAn, bMId, bRId, bBox, bName, bRefNr, bDistribution, bGate, bMime, bFollowupTo, bReplyTo, bSender *)
IF lauf IN whatsThere THEN
AppendToDat(handle, Str2Ptr(lauf), LONG(len[z]), v.lcard);
IF CatFiles.FileError < 0 THEN abort := TRUE; Abort(2, FALSE, 0, handle^.group); RETURN FALSE; END;
INC(z);
END;
END;
(* Hier mssen notfalls die unbekannten Zeilen verarbeitet werden *)
(* ..und hier kommt noch bei privaten die private-Bytes, logisch, nech? Arararahhh *)
IF handle^.group = private THEN
IF type = personal THEN
pInfo.LeseDatum := mDateStr2Long(pBSZeit^, 1);
pInfo.Status := pBSZeit^[0];
IF dataSys.bOwnMessage IN bitWishes
THEN
EXCL (bitWishes, dataSys.bOwnMessage);
EXCL (bits, dataSys.bOwnMessage);
pInfo.locked := 1C;
ELSE
pInfo.locked := 0C; (* Da eigene Msgs auch hierher kommen, mu hier nochwas neues her *)
END;
ELSIF type = own THEN
pInfo.LeseDatum := ConvertDate.CurrentDate();
(*
GetActualDate(pInfo.LeseDatum);
*)
pInfo.Status := 'N';
pInfo.locked := 1C;
END;
IF ODD(hLength) THEN (* alignen *)
INC(hLength);
AppendToDat(handle, CADR(fillBytePersonal), 1, v.lcard);
IF CatFiles.FileError < 0 THEN abort := TRUE; Abort(2, FALSE, 0, handle^.group); RETURN FALSE; END;
END;
AppendToDat(handle, ADR(pInfo), TSIZE(pInfoType), v.lcard);
IF CatFiles.FileError < 0 THEN abort := TRUE; Abort(2, FALSE, 0, handle^.group); RETURN FALSE; END;
INCL(items, mPrivateBytes);
INC(hLength, SHORT(TSIZE(pInfoType)));
ELSIF bBSZeit IN mess.whatsThere THEN
(* Status-Zeile bei OMs bei Laberfilter RAUS *)
IF pBSZeit^[0] = 'G' THEN INCL(bits, bGelesen)
ELSIF pBSZeit^[0] = 'F' THEN INCL(bits, bFiltered) END;
END;
(* ..und schon haben wir uns einen schnen Header gebastelt *)
IF type = own THEN (* Dann steht der Text in einer Datei *)
Transfer(Length, txt);
IF CatFiles.FileError < 0 THEN abort := TRUE; Abort(2, FALSE, 0, handle^.group); RETURN FALSE END;
ELSE (* Sonst wurde er per Pointer bergeben *)
Length := TextMax; (* Auf Nullbyte am Ende achten! *)
AppendToDat(handle, pText, LONG(Length), v.lcard);
IF CatFiles.FileError < 0 THEN abort := TRUE; Abort(2, FALSE, 0, handle^.group); RETURN FALSE; END;
END;
CRC := CalcIdCrc(pId^);
AppendCrc(handle, CRC);
IF CatFiles.FileError < 0 THEN abort := TRUE; Abort(0, FALSE, 0, handle^.group); RETURN FALSE; END;
(* Achtung, Reihenfolge mit AppendBlock wg. anz-Erhhung wichtig *)
END; (* WITH block *)
END; (* WITH mess *)
block.crc := CalcCrcArray(ADR(block)+ADDRESS(2), SHORT(TSIZE(pBlock))-2);
(* Neuerdings wird SICHERHEIT grogeschrieben :-) *)
AppendBlock(handle, block); (* .. und an die halbfertige *.PAR anhngen *)
IF CatFiles.FileError < 0 THEN abort := TRUE; Abort(1, FALSE, handle^.anz, handle^.group); RETURN FALSE; END;
(* Diese beiden Schreiboperationen mssen evtl. noch umgedreht werden! *)
pDupe := ADR(block)+ADDRESS(2);
pDupe^.setTerminator := Terminator;
AppendToDat(handle, pDupe, TSIZE(dupeInfoType), v.lcard);
IF CatFiles.FileError < 0 THEN abort := TRUE; Abort(2, FALSE, 0, handle^.group); RETURN FALSE; END;
END; (* ..ELSIF (type = personal) OR (type = normal) OR (type = own) *)
RETURN TRUE
END AppendMessage;
PROCEDURE dumpMess(mess : PtrRecord);
(* Fehlermeldung zu dieser Nachricht in catlog.txt ausgeben *)
VAR tmp: CatTypes.String255;
tPtr: CatTypes.Str1023Ptr;
copyLen: CARDINAL;
BEGIN
CatLog.WriteLineNTime('Fehler im Outfile:');
(*
CatLog.WriteLine('- data.i: Melde mich wg. dingenskirchen -');
*)
CatLog.WriteLine('illegales OUTFILE-Format');
CatLog.WriteLine('folgende Zeilen wurden gefunden:');
WITH mess DO
IF bId IN whatsThere THEN CatLog.WriteString('Id : '); CatLog.WriteLine(pId^); END;
IF bRefNr IN whatsThere THEN CatLog.WriteString('RefNr : '); CatLog.WriteLine(pRefNr^); END;
IF bVon IN whatsThere THEN CatLog.WriteString('Von : '); CatLog.WriteLine(pVon^); END;
IF bAn IN whatsThere THEN CatLog.WriteString('An : '); CatLog.WriteLine(pAn^); END;
IF bWegen IN whatsThere THEN CatLog.WriteString('Wegen : '); CatLog.WriteLine(pWegen^); END;
IF bEZeit IN whatsThere THEN CatLog.WriteString('EZeit : '); CatLog.WriteLine(pEZeit^); END;
IF bGruppe IN whatsThere THEN CatLog.WriteString('Gruppe: '); CatLog.WriteLine(pGruppe^); END;
IF bBSZeit IN whatsThere THEN CatLog.WriteString('BSZeit: '); CatLog.WriteLine(pBSZeit^); END;
IF bMId IN whatsThere THEN CatLog.WriteString('MId : '); CatLog.WriteLine(pMId^); END;
IF bRId IN whatsThere THEN CatLog.WriteString('RId : '); CatLog.WriteLine(pRId^); END;
IF bBox IN whatsThere THEN CatLog.WriteString('Box : '); CatLog.WriteLine(pBox^); END;
IF bName IN whatsThere THEN CatLog.WriteString('Name : '); CatLog.WriteLine(pName^); END;
IF bGate IN whatsThere THEN CatLog.WriteString('Gate : '); CatLog.WriteLine(pGate^); END;
IF bMime IN whatsThere THEN CatLog.WriteString('MIME : '); CatLog.WriteLine(pMime^); END;
IF bFollowupTo IN whatsThere THEN CatLog.WriteString('Followup-To: '); CatLog.WriteLine(pFollowupTo^); END;
IF bReplyTo IN whatsThere THEN CatLog.WriteString('Reply-To: '); CatLog.WriteLine(pReplyTo^); END;
IF bSender IN whatsThere THEN CatLog.WriteString('Sender : '); CatLog.WriteLine(pSender^); END;
IF bDistribution IN whatsThere THEN CatLog.WriteString('Distr.: '); CatLog.WriteLine(pDistribution^); END;
IF bText IN whatsThere THEN
CatLog.WriteLine('Text gefunden:');
tPtr := ADDRESS(pText);
copyLen := BinOps.LowerCard (TextMax, 230);
Strings.Copy (tPtr^, 0, copyLen, tmp, v.bool);
CatLog.WriteLine (tmp);
END;
IF bTextDatei IN whatsThere THEN CatLog.WriteLine('Text in Datei bergeben'); END;
END;
CatLog.WriteLine('- data.i: Ende der Durchsage -------------');
(*
MTE.warnAlert(mtAlerts.Alert(1, MTE.coreDumped) = 2, MTE.translation, '', '');
*)
END dumpMess;
PROCEDURE Dxreaddir (handle : lINTEGER; VAR name : ARRAY OF CHAR;
VAR xattr : FileSys.XATTR; VAR xr: lINTEGER) : lINTEGER;
VAR res: lINTEGER;
BEGIN
res := Mintbind.Dxreaddir (handle, name, xattr, xr);
IF res = LONG(MagicDOS.EInvFN)
THEN
res := Mintbind.Dreaddir (handle, name);
xr := Mintbind.Fxattr (0, name, xattr);
END;
RETURN res;
END Dxreaddir;
PROCEDURE EstimateNecessaryMemory():LONGCARD;
(* Versucht den fr den nchsten Einfgevorgang ntigen Platz zu schtzen *)
VAR myDTA : MagicDOS.DTA;
oDta : ADDRESS;
maxTab,
maxPar,
maxAmount : LONGCARD;
tmp,
mask : CatTypes.String1023;
toLower : BOOLEAN;
firstFound : BOOLEAN;
nr : CARDINAL;
dHandle : LONGINT;
nPtr : CatTypes.Str1023Ptr;
xattr : FileSys.XATTR;
err : LONGINT;
xattrErr : LONGINT;
PROCEDURE isExt(REF e : ARRAY OF CHAR):BOOLEAN;
VAR z : CARDINAL;
BEGIN
WITH myDTA DO
z := LENGTH(dFname);
RETURN ((z = 11) OR (z = 12)) &
(dFname[z-1] = e[3]) & (dFname[z-2] = e[2]) &
(dFname[z-3] = e[1]);
END;
END isExt;
BEGIN
maxAmount := 0;
nr := 0;
IF CatGlobal.isMintDomain
THEN
(* case-sensitivity vom filesystem feststellen *)
(* Zugriff ber Dopendir, Dreaddir *)
maxPar := 0;
maxTab := 0;
dHandle := Mintbind.Dopendir (DataPath, 0);
IF dHandle >= 0
THEN
nPtr := ADR (mask[4]);
firstFound := TRUE;
toLower := TRUE;
REPEAT
err := Dxreaddir (dHandle, mask, xattr, xattrErr);
IF err = 0
THEN
IF firstFound
THEN
(* Get attributes of file, follow link *)
MagicStrings.Assign (DataPath, tmp);
MagicStrings.Append (nPtr^, tmp);
toLower := 0 # (Mintbind.Dpathconf (tmp, 6));
firstFound := FALSE;
END;
IF toLower
THEN
Strings.Lower (nPtr^);
END;
(* Compare name, in den ersten 4 Bytes steht der inode *)
IF WildCards.NameMatching (nPtr^, parWild)
THEN
(* Get attributes of file, follow link *)
(*
MagicStrings.Assign (DataPath, tmp);
MagicStrings.Append (nPtr^, tmp);
err := Mintbind.Fxattr (0, tmp, xattr);
*)
IF xattrErr = 0
THEN
maxPar := BinOps.HigherLCard (maxPar, LONGCARD (xattr.size));
END;
ELSIF WildCards.NameMatching (nPtr^, tabWild)
THEN
(* Get attributes of file, follow link *)
(*
MagicStrings.Assign (DataPath, tmp);
MagicStrings.Append (nPtr^, tmp);
err := Mintbind.Fxattr (0, tmp, xattr);
*)
IF xattrErr = 0
THEN
maxTab := BinOps.HigherLCard (maxTab, LONGCARD (xattr.size));
END;
END;
END;
UNTIL err # 0;
IF err # MagicDOS.ENMFil
THEN
CatFiles.ErrorAlert (SHORT (err));
END;
v.lint := Mintbind.Dclosedir (dHandle);
maxAmount := maxTab + maxPar;
ELSE
CatFiles.ErrorAlert (SHORT (dHandle));
maxAmount := 32000; (* min size *)
END;
ELSE
oDta := MagicDOS.Fgetdta ();
MagicDOS.Fsetdta (ADR(myDTA));
MagicStrings.Assign (DataPath, mask);
MagicStrings.Append(allWild, mask);
firstFound := MagicDOS.Fsfirst (mask, {}) = 0;
maxPar := 0;
maxTab := 0;
IF firstFound THEN
REPEAT
Strings.Lower (myDTA.dFname);
IF WildCards.NameMatching (myDTA.dFname, parWild)
THEN
maxPar := BinOps.HigherLCard (maxPar, myDTA.dLength);
ELSIF WildCards.NameMatching (myDTA.dFname, tabWild)
THEN
maxTab := BinOps.HigherLCard (maxTab, myDTA.dLength);
END;
UNTIL MagicDOS.Fsnext() # 0;
END;
maxAmount := maxTab + maxPar;
MagicDOS.Fsetdta (oDta);
END;
RETURN maxAmount + minDatBuffer + TSIZE(pBlock)*additional + TSIZE(CARDINAL)*additional;
END EstimateNecessaryMemory;
(*$H+*)
(* --- Baum durchlaufen --- *)
(*
TYPE treeProc = PROCEDURE ((* handle: *) OneGroupHandle,
(* msgIdx: *) CARDINAL,
(* mess: *) pBlockPtr);
*)
PROCEDURE WalkTree (handle : OneGroupHandle; mess: CARDINAL; fromTop : BOOLEAN; proc: treeProc);
(* Allgemeine Prozedur, um einen Baum zu durchlaufen *)
VAR s : pBlock;
pb : pBlockPtr;
idx: CARDINAL;
max: CARDINAL;
err: errorType;
(* Rekursive Prozedur, aber es werden jedesmal nur zwei Bytes auf dem Stack
* abgelegt, ansonsten wird auf prozedurglobale Variablen
* zugegriffen
*)
PROCEDURE doWalk (idx : CARDINAL);
BEGIN
REPEAT
(* pb := ADR(s); *)
err := ReadBlockCrc (handle, idx, pb);
IF err = noError THEN
proc (handle, idx, pb);
idx := pb^.rightMess;
IF (pb^.downMess < empty) & (pb^.downMess <= max) THEN
doWalk (pb^.downMess);
END;
END;
UNTIL (err # noError) OR (idx > max) OR (idx >= empty);
END doWalk;
BEGIN
idx := mess;
max := handle^.anz;
pb := ADR(s);
IF max = 0 THEN RETURN ELSE DEC(max) END;
IF fromTop THEN
(* Erste Nachricht im Baum suchen *)
REPEAT
err := ReadBlockCrc (handle, idx, pb);
IF err = noError THEN
IF (pb^.upMess < notSaved) & (pb^.upMess <= max) THEN
idx := pb^.upMess
END;
END;
UNTIL (pb^.upMess >= notSaved) OR (err # noError) OR (pb^.upMess > max);
END;
IF ~fromTop THEN
(* nur "runter" lschen: aktuelle Msg behandeln und dann nur noch Tochtermsgs *)
(* pb := ADR(s); *)
err := ReadBlockCrc (handle, idx, pb);
IF err = noError THEN
proc (handle, idx, pb);
IF (pb^.downMess < empty) & (pb^.downMess <= max) THEN
doWalk (pb^.downMess);
END;
END;
ELSE
doWalk (idx);
END;
END WalkTree;
PROCEDURE WalkTree2 (handle : OneGroupHandle; mess: CARDINAL; maxUp: INTEGER; maxDepth: INTEGER; proc: treeProc);
(* Etwas weniger allgemeine Prozedur, um einen Baum zu durchlaufen.
* Es wird maximal um maxUp Ebenen nach oben gegangen, und von dort aus
* maximal maxDepth Ebenen nach unten
*)
VAR s : pBlock;
pb : pBlockPtr;
idx: CARDINAL;
max: CARDINAL;
err: errorType;
upCount : INTEGER;
(* Rekursive Prozedur, aber es werden jedesmal nur zwei Bytes auf dem Stack
* abgelegt, ansonsten wird auf prozedurglobale Variablen
* zugegriffen
*)
PROCEDURE doWalk (idx : CARDINAL; depth: INTEGER);
BEGIN
REPEAT
pb := ADR(s);
err := ReadBlockCrc (handle, idx, pb);
IF err = noError THEN
proc (handle, idx, pb);
idx := pb^.rightMess;
IF (pb^.downMess < empty) & (depth < maxDepth) THEN
doWalk (pb^.downMess, depth+1);
END;
END;
UNTIL (err # noError) OR (idx > max) OR (idx >= empty);
END doWalk;
BEGIN
idx := mess;
max := handle^.anz;
IF max = 0 THEN RETURN ELSE DEC(max) END;
(* Nach oben gehen, maximal maxUp mal *)
upCount := 0;
pb := ADR(s);
err := ReadBlock (handle, idx, pb);
IF err = noError THEN
IF pb^.downMess >= notSaved
THEN
(* In dem Fall korrigieren wir maxUp *)
maxUp := maxDepth - 1;
END;
END;
REPEAT
pb := ADR(s);
err := ReadBlock (handle, idx, pb);
IF err = noError THEN
IF pb^.upMess < notSaved THEN
idx := pb^.upMess
END;
END;
INC (upCount);
UNTIL (pb^.upMess >= notSaved) OR (upCount >= maxUp);
(* nur "runter" lschen: aktuelle Msg behandeln und dann nur noch Tochtermsgs *)
pb := ADR(s);
err := ReadBlockCrc (handle, idx, pb);
IF err = noError THEN
proc (handle, idx, pb);
IF pb^.downMess < empty THEN
doWalk (pb^.downMess, 1);
END;
END;
END WalkTree2;
(* --- Prozeduren fr die Flagsbehandlung eines Baumes --- *)
(*
TYPE updateProc = PROCEDURE((* msgIndex *) CARDINAL, (* newFlags *) BITSET);
TYPE flagchangeProc = PROCEDURE( (* oldFlags *) BITSET ) : BITSET;
*)
VAR update : updateProc;
VAR fc : flagchangeProc;
PROCEDURE helpTreeDelete(handle : OneGroupHandle; idx : CARDINAL; pb : pBlockPtr);
VAR newflags : BITSET;
BEGIN
newflags := fc(pb^.bits);
IF newflags # pb^.bits THEN
pb^.bits := newflags;
WriteBlockCrc(handle, idx, pb);
update(idx, pb^.bits);
END;
END helpTreeDelete;
PROCEDURE TreeFlags(handle : OneGroupHandle; mess : CARDINAL; fromTop : BOOLEAN;
updt : updateProc; flagchange : flagchangeProc);
(* Flags eines Baumes bearbeiten *)
BEGIN
update := updt; fc := flagchange;
WalkTree(handle, mess, fromTop, helpTreeDelete);
END TreeFlags;
BEGIN
emptyString := '';
grPosRead := FALSE;
grPos.pos := NIL;
isInSearch := FALSE;
Lists.CreateList(names, v.bool);
END data.